diff --git a/BUILD b/BUILD index aae25b18ed3..4d182f30292 100644 --- a/BUILD +++ b/BUILD @@ -774,6 +774,7 @@ grpc_cc_library( "src/core/lib/iomgr/wakeup_fd_posix.cc", "src/core/lib/iomgr/work_serializer.cc", "src/core/lib/json/json_reader.cc", + "src/core/lib/json/json_util.cc", "src/core/lib/json/json_writer.cc", "src/core/lib/slice/b64.cc", "src/core/lib/slice/percent_encoding.cc", @@ -919,6 +920,7 @@ grpc_cc_library( "src/core/lib/iomgr/wakeup_fd_posix.h", "src/core/lib/iomgr/work_serializer.h", "src/core/lib/json/json.h", + "src/core/lib/json/json_util.h", "src/core/lib/slice/b64.h", "src/core/lib/slice/percent_encoding.h", "src/core/lib/slice/slice_internal.h", @@ -1323,11 +1325,27 @@ grpc_cc_library( "envoy_ads_upb", "grpc_base", "grpc_client_channel", + "grpc_google_mesh_ca_certificate_provider_factory", "grpc_secure", "grpc_xds_api_header", ], ) +grpc_cc_library( + name = "grpc_google_mesh_ca_certificate_provider_factory", + srcs = [ + "src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc", + ], + hdrs = [ + "src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_secure", + ], +) + grpc_cc_library( name = "grpc_lb_policy_cds", srcs = [ diff --git a/BUILD.gn b/BUILD.gn index f1c05da02d5..5a5adb0c61c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -547,6 +547,8 @@ config("grpc_config") { "src/core/ext/xds/certificate_provider_registry.cc", "src/core/ext/xds/certificate_provider_registry.h", "src/core/ext/xds/certificate_provider_store.h", + "src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc", + "src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h", "src/core/ext/xds/xds_api.cc", "src/core/ext/xds/xds_api.h", "src/core/ext/xds/xds_bootstrap.cc", @@ -790,6 +792,8 @@ config("grpc_config") { "src/core/lib/iomgr/work_serializer.h", "src/core/lib/json/json.h", "src/core/lib/json/json_reader.cc", + "src/core/lib/json/json_util.cc", + "src/core/lib/json/json_util.h", "src/core/lib/json/json_writer.cc", "src/core/lib/security/authorization/authorization_engine.cc", "src/core/lib/security/authorization/authorization_engine.h", diff --git a/CMakeLists.txt b/CMakeLists.txt index 35d78737639..b4fbaf2538e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -822,6 +822,7 @@ if(gRPC_BUILD_TESTS) add_dependencies(buildtests_cxx global_config_env_test) endif() add_dependencies(buildtests_cxx global_config_test) + add_dependencies(buildtests_cxx google_mesh_ca_certificate_provider_factory_test) add_dependencies(buildtests_cxx grpc_cli) add_dependencies(buildtests_cxx grpc_tls_certificate_distributor_test) add_dependencies(buildtests_cxx grpc_tls_credentials_options_test) @@ -1598,6 +1599,7 @@ add_library(grpc src/core/ext/upb-generated/udpa/data/orca/v1/orca_load_report.upb.c src/core/ext/upb-generated/validate/validate.upb.c src/core/ext/xds/certificate_provider_registry.cc + src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc src/core/ext/xds/xds_api.cc src/core/ext/xds/xds_bootstrap.cc src/core/ext/xds/xds_client.cc @@ -1725,6 +1727,7 @@ add_library(grpc src/core/lib/iomgr/wakeup_fd_posix.cc src/core/lib/iomgr/work_serializer.cc src/core/lib/json/json_reader.cc + src/core/lib/json/json_util.cc src/core/lib/json/json_writer.cc src/core/lib/security/authorization/authorization_engine.cc src/core/lib/security/authorization/evaluate_args.cc @@ -2340,6 +2343,7 @@ add_library(grpc_unsecure src/core/lib/iomgr/wakeup_fd_posix.cc src/core/lib/iomgr/work_serializer.cc src/core/lib/json/json_reader.cc + src/core/lib/json/json_util.cc src/core/lib/json/json_writer.cc src/core/lib/slice/b64.cc src/core/lib/slice/percent_encoding.cc @@ -11096,6 +11100,45 @@ target_link_libraries(global_config_test ) +endif() +if(gRPC_BUILD_TESTS) + +add_executable(google_mesh_ca_certificate_provider_factory_test + test/core/xds/google_mesh_ca_certificate_provider_factory_test.cc + third_party/googletest/googletest/src/gtest-all.cc + third_party/googletest/googlemock/src/gmock-all.cc +) + +target_include_directories(google_mesh_ca_certificate_provider_factory_test + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR} + ${_gRPC_RE2_INCLUDE_DIR} + ${_gRPC_SSL_INCLUDE_DIR} + ${_gRPC_UPB_GENERATED_DIR} + ${_gRPC_UPB_GRPC_GENERATED_DIR} + ${_gRPC_UPB_INCLUDE_DIR} + ${_gRPC_ZLIB_INCLUDE_DIR} + third_party/googletest/googletest/include + third_party/googletest/googletest + third_party/googletest/googlemock/include + third_party/googletest/googlemock + ${_gRPC_PROTO_GENS_DIR} +) + +target_link_libraries(google_mesh_ca_certificate_provider_factory_test + ${_gRPC_PROTOBUF_LIBRARIES} + ${_gRPC_ALLTARGETS_LIBRARIES} + grpc_test_util + grpc + gpr + address_sorting + upb + ${_gRPC_GFLAGS_LIBRARIES} +) + + endif() if(gRPC_BUILD_TESTS) diff --git a/Makefile b/Makefile index 609b1079cfa..8e94ae2d76a 100644 --- a/Makefile +++ b/Makefile @@ -2003,6 +2003,7 @@ LIBGRPC_SRC = \ src/core/ext/upb-generated/udpa/data/orca/v1/orca_load_report.upb.c \ src/core/ext/upb-generated/validate/validate.upb.c \ src/core/ext/xds/certificate_provider_registry.cc \ + src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc \ src/core/ext/xds/xds_api.cc \ src/core/ext/xds/xds_bootstrap.cc \ src/core/ext/xds/xds_client.cc \ @@ -2130,6 +2131,7 @@ LIBGRPC_SRC = \ src/core/lib/iomgr/wakeup_fd_posix.cc \ src/core/lib/iomgr/work_serializer.cc \ src/core/lib/json/json_reader.cc \ + src/core/lib/json/json_util.cc \ src/core/lib/json/json_writer.cc \ src/core/lib/security/authorization/authorization_engine.cc \ src/core/lib/security/authorization/evaluate_args.cc \ @@ -2612,6 +2614,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/lib/iomgr/wakeup_fd_posix.cc \ src/core/lib/iomgr/work_serializer.cc \ src/core/lib/json/json_reader.cc \ + src/core/lib/json/json_util.cc \ src/core/lib/json/json_writer.cc \ src/core/lib/slice/b64.cc \ src/core/lib/slice/percent_encoding.cc \ @@ -4567,6 +4570,7 @@ src/core/ext/upb-generated/udpa/annotations/sensitive.upb.c: $(OPENSSL_DEP) src/core/ext/upb-generated/udpa/annotations/status.upb.c: $(OPENSSL_DEP) src/core/ext/upb-generated/udpa/annotations/versioning.upb.c: $(OPENSSL_DEP) src/core/ext/xds/certificate_provider_registry.cc: $(OPENSSL_DEP) +src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_api.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_bootstrap.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_client.cc: $(OPENSSL_DEP) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 5292f07fd48..f5bd90cd61b 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -538,6 +538,7 @@ libs: - src/core/ext/xds/certificate_provider_factory.h - src/core/ext/xds/certificate_provider_registry.h - src/core/ext/xds/certificate_provider_store.h + - src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h - src/core/ext/xds/xds_api.h - src/core/ext/xds/xds_bootstrap.h - src/core/ext/xds/xds_channel_args.h @@ -654,6 +655,7 @@ libs: - src/core/lib/iomgr/wakeup_fd_posix.h - src/core/lib/iomgr/work_serializer.h - src/core/lib/json/json.h + - src/core/lib/json/json_util.h - src/core/lib/security/authorization/authorization_engine.h - src/core/lib/security/authorization/evaluate_args.h - src/core/lib/security/authorization/mock_cel/activation.h @@ -945,6 +947,7 @@ libs: - src/core/ext/upb-generated/udpa/data/orca/v1/orca_load_report.upb.c - src/core/ext/upb-generated/validate/validate.upb.c - src/core/ext/xds/certificate_provider_registry.cc + - src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc - src/core/ext/xds/xds_api.cc - src/core/ext/xds/xds_bootstrap.cc - src/core/ext/xds/xds_client.cc @@ -1072,6 +1075,7 @@ libs: - src/core/lib/iomgr/wakeup_fd_posix.cc - src/core/lib/iomgr/work_serializer.cc - src/core/lib/json/json_reader.cc + - src/core/lib/json/json_util.cc - src/core/lib/json/json_writer.cc - src/core/lib/security/authorization/authorization_engine.cc - src/core/lib/security/authorization/evaluate_args.cc @@ -1541,6 +1545,7 @@ libs: - src/core/lib/iomgr/wakeup_fd_posix.h - src/core/lib/iomgr/work_serializer.h - src/core/lib/json/json.h + - src/core/lib/json/json_util.h - src/core/lib/slice/b64.h - src/core/lib/slice/percent_encoding.h - src/core/lib/slice/slice_internal.h @@ -1813,6 +1818,7 @@ libs: - src/core/lib/iomgr/wakeup_fd_posix.cc - src/core/lib/iomgr/work_serializer.cc - src/core/lib/json/json_reader.cc + - src/core/lib/json/json_util.cc - src/core/lib/json/json_writer.cc - src/core/lib/slice/b64.cc - src/core/lib/slice/percent_encoding.cc @@ -5821,6 +5827,19 @@ targets: - address_sorting - upb uses_polling: false +- name: google_mesh_ca_certificate_provider_factory_test + gtest: true + build: test + language: c++ + headers: [] + src: + - test/core/xds/google_mesh_ca_certificate_provider_factory_test.cc + deps: + - grpc_test_util + - grpc + - gpr + - address_sorting + - upb - name: grpc_cli build: test run: false diff --git a/config.m4 b/config.m4 index 40c9c782c9b..8296eac7ec4 100644 --- a/config.m4 +++ b/config.m4 @@ -224,6 +224,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/upb-generated/udpa/data/orca/v1/orca_load_report.upb.c \ src/core/ext/upb-generated/validate/validate.upb.c \ src/core/ext/xds/certificate_provider_registry.cc \ + src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc \ src/core/ext/xds/xds_api.cc \ src/core/ext/xds/xds_bootstrap.cc \ src/core/ext/xds/xds_client.cc \ @@ -390,6 +391,7 @@ if test "$PHP_GRPC" != "no"; then src/core/lib/iomgr/wakeup_fd_posix.cc \ src/core/lib/iomgr/work_serializer.cc \ src/core/lib/json/json_reader.cc \ + src/core/lib/json/json_util.cc \ src/core/lib/json/json_writer.cc \ src/core/lib/profiling/basic_timers.cc \ src/core/lib/profiling/stap_timers.cc \ diff --git a/config.w32 b/config.w32 index fb5633d68ad..ddc90795dcb 100644 --- a/config.w32 +++ b/config.w32 @@ -191,6 +191,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\upb-generated\\udpa\\data\\orca\\v1\\orca_load_report.upb.c " + "src\\core\\ext\\upb-generated\\validate\\validate.upb.c " + "src\\core\\ext\\xds\\certificate_provider_registry.cc " + + "src\\core\\ext\\xds\\google_mesh_ca_certificate_provider_factory.cc " + "src\\core\\ext\\xds\\xds_api.cc " + "src\\core\\ext\\xds\\xds_bootstrap.cc " + "src\\core\\ext\\xds\\xds_client.cc " + @@ -357,6 +358,7 @@ if (PHP_GRPC != "no") { "src\\core\\lib\\iomgr\\wakeup_fd_posix.cc " + "src\\core\\lib\\iomgr\\work_serializer.cc " + "src\\core\\lib\\json\\json_reader.cc " + + "src\\core\\lib\\json\\json_util.cc " + "src\\core\\lib\\json\\json_writer.cc " + "src\\core\\lib\\profiling\\basic_timers.cc " + "src\\core\\lib\\profiling\\stap_timers.cc " + diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index 02f640f68fa..54c226e2b61 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -360,6 +360,7 @@ Pod::Spec.new do |s| 'src/core/ext/xds/certificate_provider_factory.h', 'src/core/ext/xds/certificate_provider_registry.h', 'src/core/ext/xds/certificate_provider_store.h', + 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h', 'src/core/ext/xds/xds_api.h', 'src/core/ext/xds/xds_bootstrap.h', 'src/core/ext/xds/xds_channel_args.h', @@ -503,6 +504,7 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/wakeup_fd_posix.h', 'src/core/lib/iomgr/work_serializer.h', 'src/core/lib/json/json.h', + 'src/core/lib/json/json_util.h', 'src/core/lib/profiling/timers.h', 'src/core/lib/security/authorization/authorization_engine.h', 'src/core/lib/security/authorization/evaluate_args.h', @@ -864,6 +866,7 @@ Pod::Spec.new do |s| 'src/core/ext/xds/certificate_provider_factory.h', 'src/core/ext/xds/certificate_provider_registry.h', 'src/core/ext/xds/certificate_provider_store.h', + 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h', 'src/core/ext/xds/xds_api.h', 'src/core/ext/xds/xds_bootstrap.h', 'src/core/ext/xds/xds_channel_args.h', @@ -1007,6 +1010,7 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/wakeup_fd_posix.h', 'src/core/lib/iomgr/work_serializer.h', 'src/core/lib/json/json.h', + 'src/core/lib/json/json_util.h', 'src/core/lib/profiling/timers.h', 'src/core/lib/security/authorization/authorization_engine.h', 'src/core/lib/security/authorization/evaluate_args.h', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 2a52e0a7acb..36a77bad83e 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -533,6 +533,8 @@ Pod::Spec.new do |s| 'src/core/ext/xds/certificate_provider_registry.cc', 'src/core/ext/xds/certificate_provider_registry.h', 'src/core/ext/xds/certificate_provider_store.h', + 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc', + 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h', 'src/core/ext/xds/xds_api.cc', 'src/core/ext/xds/xds_api.h', 'src/core/ext/xds/xds_bootstrap.cc', @@ -842,6 +844,8 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/work_serializer.h', 'src/core/lib/json/json.h', 'src/core/lib/json/json_reader.cc', + 'src/core/lib/json/json_util.cc', + 'src/core/lib/json/json_util.h', 'src/core/lib/json/json_writer.cc', 'src/core/lib/profiling/basic_timers.cc', 'src/core/lib/profiling/stap_timers.cc', @@ -1289,6 +1293,7 @@ Pod::Spec.new do |s| 'src/core/ext/xds/certificate_provider_factory.h', 'src/core/ext/xds/certificate_provider_registry.h', 'src/core/ext/xds/certificate_provider_store.h', + 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h', 'src/core/ext/xds/xds_api.h', 'src/core/ext/xds/xds_bootstrap.h', 'src/core/ext/xds/xds_channel_args.h', @@ -1432,6 +1437,7 @@ Pod::Spec.new do |s| 'src/core/lib/iomgr/wakeup_fd_posix.h', 'src/core/lib/iomgr/work_serializer.h', 'src/core/lib/json/json.h', + 'src/core/lib/json/json_util.h', 'src/core/lib/profiling/timers.h', 'src/core/lib/security/authorization/authorization_engine.h', 'src/core/lib/security/authorization/evaluate_args.h', diff --git a/grpc.gemspec b/grpc.gemspec index 6826e2435cf..a27b1fe4c02 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -451,6 +451,8 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/xds/certificate_provider_registry.cc ) s.files += %w( src/core/ext/xds/certificate_provider_registry.h ) s.files += %w( src/core/ext/xds/certificate_provider_store.h ) + s.files += %w( src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc ) + s.files += %w( src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h ) s.files += %w( src/core/ext/xds/xds_api.cc ) s.files += %w( src/core/ext/xds/xds_api.h ) s.files += %w( src/core/ext/xds/xds_bootstrap.cc ) @@ -760,6 +762,8 @@ Gem::Specification.new do |s| s.files += %w( src/core/lib/iomgr/work_serializer.h ) s.files += %w( src/core/lib/json/json.h ) s.files += %w( src/core/lib/json/json_reader.cc ) + s.files += %w( src/core/lib/json/json_util.cc ) + s.files += %w( src/core/lib/json/json_util.h ) s.files += %w( src/core/lib/json/json_writer.cc ) s.files += %w( src/core/lib/profiling/basic_timers.cc ) s.files += %w( src/core/lib/profiling/stap_timers.cc ) diff --git a/grpc.gyp b/grpc.gyp index 426bab801d4..f7e8e3de654 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -629,6 +629,7 @@ 'src/core/ext/upb-generated/udpa/data/orca/v1/orca_load_report.upb.c', 'src/core/ext/upb-generated/validate/validate.upb.c', 'src/core/ext/xds/certificate_provider_registry.cc', + 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc', 'src/core/ext/xds/xds_api.cc', 'src/core/ext/xds/xds_bootstrap.cc', 'src/core/ext/xds/xds_client.cc', @@ -756,6 +757,7 @@ 'src/core/lib/iomgr/wakeup_fd_posix.cc', 'src/core/lib/iomgr/work_serializer.cc', 'src/core/lib/json/json_reader.cc', + 'src/core/lib/json/json_util.cc', 'src/core/lib/json/json_writer.cc', 'src/core/lib/security/authorization/authorization_engine.cc', 'src/core/lib/security/authorization/evaluate_args.cc', @@ -1201,6 +1203,7 @@ 'src/core/lib/iomgr/wakeup_fd_posix.cc', 'src/core/lib/iomgr/work_serializer.cc', 'src/core/lib/json/json_reader.cc', + 'src/core/lib/json/json_util.cc', 'src/core/lib/json/json_writer.cc', 'src/core/lib/slice/b64.cc', 'src/core/lib/slice/percent_encoding.cc', diff --git a/package.xml b/package.xml index 3f6e0e3aef7..dcb8a80f9a8 100644 --- a/package.xml +++ b/package.xml @@ -431,6 +431,8 @@ + + @@ -740,6 +742,8 @@ + + diff --git a/src/core/ext/filters/client_channel/resolver_result_parsing.cc b/src/core/ext/filters/client_channel/resolver_result_parsing.cc index 5203748388d..cbf8de83f3d 100644 --- a/src/core/ext/filters/client_channel/resolver_result_parsing.cc +++ b/src/core/ext/filters/client_channel/resolver_result_parsing.cc @@ -38,6 +38,7 @@ #include "src/core/lib/channel/status_util.h" #include "src/core/lib/gpr/string.h" #include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/json/json_util.h" #include "src/core/lib/uri/uri_parser.h" // As per the retry design, we do not allow more than 5 retry attempts. @@ -62,38 +63,6 @@ void ClientChannelServiceConfigParser::Register() { namespace { -// Parses a JSON field of the form generated for a google.proto.Duration -// proto message, as per: -// https://developers.google.com/protocol-buffers/docs/proto3#json -bool ParseDuration(const Json& field, grpc_millis* duration) { - if (field.type() != Json::Type::STRING) return false; - size_t len = field.string_value().size(); - if (field.string_value()[len - 1] != 's') return false; - grpc_core::UniquePtr buf(gpr_strdup(field.string_value().c_str())); - *(buf.get() + len - 1) = '\0'; // Remove trailing 's'. - char* decimal_point = strchr(buf.get(), '.'); - int nanos = 0; - if (decimal_point != nullptr) { - *decimal_point = '\0'; - nanos = gpr_parse_nonnegative_int(decimal_point + 1); - if (nanos == -1) { - return false; - } - int num_digits = static_cast(strlen(decimal_point + 1)); - if (num_digits > 9) { // We don't accept greater precision than nanos. - return false; - } - for (int i = 0; i < (9 - num_digits); ++i) { - nanos *= 10; - } - } - int seconds = - decimal_point == buf.get() ? 0 : gpr_parse_nonnegative_int(buf.get()); - if (seconds == -1) return false; - *duration = seconds * GPR_MS_PER_SEC + nanos / GPR_NS_PER_MS; - return true; -} - std::unique_ptr ParseRetryPolicy( const Json& json, grpc_error** error) { GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE); @@ -128,7 +97,7 @@ std::unique_ptr ParseRetryPolicy( // Parse initialBackoff. it = json.object_value().find("initialBackoff"); if (it != json.object_value().end()) { - if (!ParseDuration(it->second, &retry_policy->initial_backoff)) { + if (!ParseDurationFromJson(it->second, &retry_policy->initial_backoff)) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "field:initialBackoff error:Failed to parse")); } else if (retry_policy->initial_backoff == 0) { @@ -139,7 +108,7 @@ std::unique_ptr ParseRetryPolicy( // Parse maxBackoff. it = json.object_value().find("maxBackoff"); if (it != json.object_value().end()) { - if (!ParseDuration(it->second, &retry_policy->max_backoff)) { + if (!ParseDurationFromJson(it->second, &retry_policy->max_backoff)) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "field:maxBackoff error:failed to parse")); } else if (retry_policy->max_backoff == 0) { @@ -416,7 +385,7 @@ ClientChannelServiceConfigParser::ParsePerMethodParams(const Json& json, // Parse timeout. it = json.object_value().find("timeout"); if (it != json.object_value().end()) { - if (!ParseDuration(it->second, &timeout)) { + if (!ParseDurationFromJson(it->second, &timeout)) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( "field:timeout error:Failed parsing")); }; diff --git a/src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc b/src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc new file mode 100644 index 00000000000..8e1f7b5a456 --- /dev/null +++ b/src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc @@ -0,0 +1,377 @@ +// +// +// 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/ext/xds/google_mesh_ca_certificate_provider_factory.h" + +#include +#include + +#include "absl/strings/str_cat.h" + +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/json/json_util.h" + +namespace grpc_core { + +namespace { + +const char* kMeshCaPlugin = "meshCA"; + +// +// Helper functions for extracting types from JSON +// +template +bool ExtractJsonType(const Json& json, const std::string& field_name, + NumericType* output, ErrorVectorType* error_list) { + static_assert(std::is_integral::value, "Integral required"); + if (json.type() != Json::Type::NUMBER) { + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:type should be NUMBER") + .c_str())); + return false; + } + std::istringstream ss(json.string_value()); + ss >> *output; + // The JSON parsing API should have dealt with parsing errors, but check + // anyway + if (GPR_UNLIKELY(ss.bad())) { + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:failed to parse.").c_str())); + return false; + } + return true; +} + +template +bool ExtractJsonType(const Json& json, const std::string& field_name, + bool* output, ErrorVectorType* error_list) { + switch (json.type()) { + case Json::Type::JSON_TRUE: + *output = true; + return true; + case Json::Type::JSON_FALSE: + *output = false; + return true; + default: + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:type should be BOOLEAN") + .c_str())); + return false; + } +} + +template +bool ExtractJsonType(const Json& json, const std::string& field_name, + std::string* output, ErrorVectorType* error_list) { + if (json.type() != Json::Type::STRING) { + *output = ""; + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:type should be STRING") + .c_str())); + return false; + } + *output = json.string_value(); + return true; +} + +template +bool ExtractJsonType(const Json& json, const std::string& field_name, + const Json::Array** output, ErrorVectorType* error_list) { + if (json.type() != Json::Type::ARRAY) { + *output = nullptr; + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:type should be ARRAY") + .c_str())); + return false; + } + *output = &json.array_value(); + return true; +} + +template +bool ExtractJsonType(const Json& json, const std::string& field_name, + const Json::Object** output, ErrorVectorType* error_list) { + if (json.type() != Json::Type::OBJECT) { + *output = nullptr; + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:type should be OBJECT") + .c_str())); + return false; + } + *output = &json.object_value(); + return true; +} + +template +bool ExtractJsonType(const Json& json, const std::string& field_name, + grpc_millis* output, ErrorVectorType* error_list) { + if (!ParseDurationFromJson(json, output)) { + *output = GRPC_MILLIS_INF_PAST; + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, + " error:type should be STRING of the form given by " + "google.proto.Duration.") + .c_str())); + return false; + } + return true; +} + +template +bool ParseJsonObjectField(const Json::Object& object, + const std::string& field_name, T* output, + ErrorVectorType* error_list, bool optional = false) { + auto it = object.find(field_name); + if (it == object.end()) { + if (!optional) { + error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("field:", field_name, " error:does not exist.") + .c_str())); + } + return false; + } + auto& child_object_json = it->second; + return ExtractJsonType(child_object_json, field_name, output, error_list); +} + +} // namespace + +// +// GoogleMeshCaCertificateProviderFactory::Config +// + +const char* GoogleMeshCaCertificateProviderFactory::Config::name() const { + return kMeshCaPlugin; +} + +std::vector +GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectStsService( + const Json::Object& sts_service) { + std::vector error_list_sts_service; + if (!ParseJsonObjectField(sts_service, "token_exchange_service_uri", + &sts_config_.token_exchange_service_uri, + &error_list_sts_service, true)) { + sts_config_.token_exchange_service_uri = + "securetoken.googleapis.com"; // default + } + ParseJsonObjectField(sts_service, "resource", &sts_config_.resource, + &error_list_sts_service, true); + ParseJsonObjectField(sts_service, "audience", &sts_config_.audience, + &error_list_sts_service, true); + if (!ParseJsonObjectField(sts_service, "scope", &sts_config_.scope, + &error_list_sts_service, true)) { + sts_config_.scope = + "https://www.googleapis.com/auth/cloud-platform"; // default + } + ParseJsonObjectField(sts_service, "requested_token_type", + &sts_config_.requested_token_type, + &error_list_sts_service, true); + ParseJsonObjectField(sts_service, "subject_token_path", + &sts_config_.subject_token_path, + &error_list_sts_service); + ParseJsonObjectField(sts_service, "subject_token_type", + &sts_config_.subject_token_type, + &error_list_sts_service); + ParseJsonObjectField(sts_service, "actor_token_path", + &sts_config_.actor_token_path, &error_list_sts_service, + true); + ParseJsonObjectField(sts_service, "actor_token_type", + &sts_config_.actor_token_type, &error_list_sts_service, + true); + return error_list_sts_service; +} + +std::vector +GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectCallCredentials( + const Json::Object& call_credentials) { + std::vector error_list_call_credentials; + const Json::Object* sts_service = nullptr; + if (ParseJsonObjectField(call_credentials, "sts_service", &sts_service, + &error_list_call_credentials)) { + std::vector error_list_sts_service = + ParseJsonObjectStsService(*sts_service); + if (!error_list_sts_service.empty()) { + error_list_call_credentials.push_back(GRPC_ERROR_CREATE_FROM_VECTOR( + "field:sts_service", &error_list_sts_service)); + } + } + return error_list_call_credentials; +} + +std::vector +GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectGoogleGrpc( + const Json::Object& google_grpc) { + std::vector error_list_google_grpc; + if (!ParseJsonObjectField(google_grpc, "target_uri", &endpoint_, + &error_list_google_grpc, true)) { + endpoint_ = "meshca.googleapis.com"; // Default target + } + const Json::Array* call_credentials_array = nullptr; + if (ParseJsonObjectField(google_grpc, "call_credentials", + &call_credentials_array, &error_list_google_grpc)) { + if (call_credentials_array->size() != 1) { + error_list_google_grpc.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "field:call_credentials error:Need exactly one entry.")); + } else { + const Json::Object* call_credentials = nullptr; + if (ExtractJsonType((*call_credentials_array)[0], "call_credentials[0]", + &call_credentials, &error_list_google_grpc)) { + std::vector error_list_call_credentials = + ParseJsonObjectCallCredentials(*call_credentials); + if (!error_list_call_credentials.empty()) { + error_list_google_grpc.push_back(GRPC_ERROR_CREATE_FROM_VECTOR( + "field:call_credentials", &error_list_call_credentials)); + } + } + } + } + + return error_list_google_grpc; +} + +std::vector +GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectGrpcServices( + const Json::Object& grpc_service) { + std::vector error_list_grpc_services; + const Json::Object* google_grpc = nullptr; + if (ParseJsonObjectField(grpc_service, "google_grpc", &google_grpc, + &error_list_grpc_services)) { + std::vector error_list_google_grpc = + ParseJsonObjectGoogleGrpc(*google_grpc); + if (!error_list_google_grpc.empty()) { + error_list_grpc_services.push_back(GRPC_ERROR_CREATE_FROM_VECTOR( + "field:google_grpc", &error_list_google_grpc)); + } + } + if (!ParseJsonObjectField(grpc_service, "timeout", &timeout_, + &error_list_grpc_services, true)) { + timeout_ = 10 * 1000; // 10sec default + } + return error_list_grpc_services; +} + +std::vector +GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectServer( + const Json::Object& server) { + std::vector error_list_server; + std::string api_type; + if (ParseJsonObjectField(server, "api_type", &api_type, &error_list_server, + true)) { + if (api_type != "GRPC") { + error_list_server.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "field:api_type error:Only GRPC is supported")); + } + } + const Json::Array* grpc_services = nullptr; + if (ParseJsonObjectField(server, "grpc_services", &grpc_services, + &error_list_server)) { + if (grpc_services->size() != 1) { + error_list_server.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "field:grpc_services error:Need exactly one entry")); + } else { + const Json::Object* grpc_service = nullptr; + if (ExtractJsonType((*grpc_services)[0], "grpc_services[0]", + &grpc_service, &error_list_server)) { + std::vector error_list_grpc_services = + ParseJsonObjectGrpcServices(*grpc_service); + if (!error_list_grpc_services.empty()) { + error_list_server.push_back(GRPC_ERROR_CREATE_FROM_VECTOR( + "field:grpc_services", &error_list_grpc_services)); + } + } + } + } + return error_list_server; +} + +std::unique_ptr +GoogleMeshCaCertificateProviderFactory::Config::Parse(const Json& config_json, + grpc_error** error) { + auto config = + absl::make_unique(); + if (config_json.type() != Json::Type::OBJECT) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "error:config type should be OBJECT."); + return nullptr; + } + std::vector error_list; + const Json::Object* server = nullptr; + if (ParseJsonObjectField(config_json.object_value(), "server", &server, + &error_list)) { + std::vector error_list_server = + config->ParseJsonObjectServer(*server); + if (!error_list_server.empty()) { + error_list.push_back( + GRPC_ERROR_CREATE_FROM_VECTOR("field:server", &error_list_server)); + } + } + if (!ParseJsonObjectField(config_json.object_value(), "certificate_lifetime", + &config->certificate_lifetime_, &error_list, + true)) { + config->certificate_lifetime_ = 24 * 60 * 60 * 1000; // 24hrs default + } + if (!ParseJsonObjectField(config_json.object_value(), "renewal_grace_period", + &config->renewal_grace_period_, &error_list, + true)) { + config->renewal_grace_period_ = 12 * 60 * 60 * 1000; // 12hrs default + } + std::string key_type; + if (ParseJsonObjectField(config_json.object_value(), "key_type", &key_type, + &error_list, true)) { + if (key_type != "RSA") { + error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "field:key_type error:Only RSA is supported.")); + } + } + if (!ParseJsonObjectField(config_json.object_value(), "key_size", + &config->key_size_, &error_list, true)) { + config->key_size_ = 2048; // default 2048 bit key size + } + if (!ParseJsonObjectField(config_json.object_value(), "location", + &config->location_, &error_list, true)) { + // GCE/GKE Metadata server needs to be contacted to get the value. + } + if (!error_list.empty()) { + *error = GRPC_ERROR_CREATE_FROM_VECTOR( + "Error parsing google Mesh CA config", &error_list); + return nullptr; + } + return config; +} + +// +// GoogleMeshCaCertificateProviderFactory +// + +const char* GoogleMeshCaCertificateProviderFactory::name() const { + return kMeshCaPlugin; +} + +std::unique_ptr +GoogleMeshCaCertificateProviderFactory::CreateCertificateProviderConfig( + const Json& config_json, grpc_error** error) { + return GoogleMeshCaCertificateProviderFactory::Config::Parse(config_json, + error); +} + +} // namespace grpc_core diff --git a/src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h b/src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h new file mode 100644 index 00000000000..a48b1c65b26 --- /dev/null +++ b/src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h @@ -0,0 +1,102 @@ +// +// +// 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_EXT_XDS_GOOGLE_MESH_CA_CERTIFICATE_PROVIDER_FACTORY_H +#define GRPC_CORE_EXT_XDS_GOOGLE_MESH_CA_CERTIFICATE_PROVIDER_FACTORY_H + +#include + +#include "src/core/ext/xds/certificate_provider_factory.h" +#include "src/core/lib/backoff/backoff.h" +#include "src/core/lib/gprpp/ref_counted.h" + +namespace grpc_core { + +class GoogleMeshCaCertificateProviderFactory + : public CertificateProviderFactory { + public: + class Config : public CertificateProviderFactory::Config { + public: + struct StsConfig { + std::string token_exchange_service_uri; + std::string resource; + std::string audience; + std::string scope; + std::string requested_token_type; + std::string subject_token_path; + std::string subject_token_type; + std::string actor_token_path; + std::string actor_token_type; + }; + + const char* name() const override; + + const std::string& endpoint() const { return endpoint_; } + + const StsConfig& sts_config() const { return sts_config_; } + + grpc_millis timeout() const { return timeout_; } + + grpc_millis certificate_lifetime() const { return certificate_lifetime_; } + + grpc_millis renewal_grace_period() const { return renewal_grace_period_; } + + uint32_t key_size() const { return key_size_; } + + const std::string& location() const { return location_; } + + static std::unique_ptr Parse(const Json& config_json, + grpc_error** error); + + private: + // Helpers for parsing the config + std::vector ParseJsonObjectStsService( + const Json::Object& sts_service); + std::vector ParseJsonObjectCallCredentials( + const Json::Object& call_credentials); + std::vector ParseJsonObjectGoogleGrpc( + const Json::Object& google_grpc); + std::vector ParseJsonObjectGrpcServices( + const Json::Object& grpc_service); + std::vector ParseJsonObjectServer(const Json::Object& server); + + std::string endpoint_; + StsConfig sts_config_; + grpc_millis timeout_; + grpc_millis certificate_lifetime_; + grpc_millis renewal_grace_period_; + uint32_t key_size_; + std::string location_; + }; + + const char* name() const override; + + std::unique_ptr + CreateCertificateProviderConfig(const Json& config_json, + grpc_error** error) override; + + RefCountedPtr CreateCertificateProvider( + std::unique_ptr config) override { + // TODO(yashykt) : To be implemented + return nullptr; + } +}; + +} // namespace grpc_core + +#endif // GRPC_CORE_EXT_XDS_GOOGLE_MESH_CA_CERTIFICATE_PROVIDER_FACTORY_H diff --git a/src/core/lib/json/json_util.cc b/src/core/lib/json/json_util.cc new file mode 100644 index 00000000000..1c90aeb5b47 --- /dev/null +++ b/src/core/lib/json/json_util.cc @@ -0,0 +1,58 @@ +// +// +// 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/json/json_util.h" + +#include + +#include "src/core/lib/gpr/string.h" + +namespace grpc_core { + +bool ParseDurationFromJson(const Json& field, grpc_millis* duration) { + if (field.type() != Json::Type::STRING) return false; + size_t len = field.string_value().size(); + if (field.string_value()[len - 1] != 's') return false; + grpc_core::UniquePtr buf(gpr_strdup(field.string_value().c_str())); + *(buf.get() + len - 1) = '\0'; // Remove trailing 's'. + char* decimal_point = strchr(buf.get(), '.'); + int nanos = 0; + if (decimal_point != nullptr) { + *decimal_point = '\0'; + nanos = gpr_parse_nonnegative_int(decimal_point + 1); + if (nanos == -1) { + return false; + } + int num_digits = static_cast(strlen(decimal_point + 1)); + if (num_digits > 9) { // We don't accept greater precision than nanos. + return false; + } + for (int i = 0; i < (9 - num_digits); ++i) { + nanos *= 10; + } + } + int seconds = + decimal_point == buf.get() ? 0 : gpr_parse_nonnegative_int(buf.get()); + if (seconds == -1) return false; + *duration = seconds * GPR_MS_PER_SEC + nanos / GPR_NS_PER_MS; + return true; +} + +} // namespace grpc_core diff --git a/src/core/lib/json/json_util.h b/src/core/lib/json/json_util.h new file mode 100644 index 00000000000..071087f7658 --- /dev/null +++ b/src/core/lib/json/json_util.h @@ -0,0 +1,37 @@ +// +// +// 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_JSON_JSON_UTIL_H +#define GRPC_CORE_LIB_JSON_JSON_UTIL_H + +#include + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/json/json.h" + +namespace grpc_core { + +// Parses a JSON field of the form generated for a google.proto.Duration +// proto message, as per: +// https://developers.google.com/protocol-buffers/docs/proto3#json +// Returns true on success, false otherwise. +bool ParseDurationFromJson(const Json& field, grpc_millis* duration); + +} // namespace grpc_core + +#endif // GRPC_CORE_LIB_JSON_JSON_UTIL_H diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index f68603d8a5e..dc5e0fd167c 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -200,6 +200,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/upb-generated/udpa/data/orca/v1/orca_load_report.upb.c', 'src/core/ext/upb-generated/validate/validate.upb.c', 'src/core/ext/xds/certificate_provider_registry.cc', + 'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc', 'src/core/ext/xds/xds_api.cc', 'src/core/ext/xds/xds_bootstrap.cc', 'src/core/ext/xds/xds_client.cc', @@ -366,6 +367,7 @@ CORE_SOURCE_FILES = [ 'src/core/lib/iomgr/wakeup_fd_posix.cc', 'src/core/lib/iomgr/work_serializer.cc', 'src/core/lib/json/json_reader.cc', + 'src/core/lib/json/json_util.cc', 'src/core/lib/json/json_writer.cc', 'src/core/lib/profiling/basic_timers.cc', 'src/core/lib/profiling/stap_timers.cc', diff --git a/test/core/security/authorization_engine_test.cc b/test/core/security/authorization_engine_test.cc index 95d2a1e19d2..4b456a93550 100644 --- a/test/core/security/authorization_engine_test.cc +++ b/test/core/security/authorization_engine_test.cc @@ -77,4 +77,4 @@ TEST_F(AuthorizationEngineTest, CreateEngineFailWrongPolicyOrder) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/test/core/xds/BUILD b/test/core/xds/BUILD new file mode 100644 index 00000000000..1aad0470b3e --- /dev/null +++ b/test/core/xds/BUILD @@ -0,0 +1,31 @@ +# 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. + +load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package") + +grpc_package(name = "test/core/client_channel") + +licenses(["notice"]) + +grpc_cc_test( + name = "google_mesh_ca_certificate_provider_factory_test", + srcs = ["google_mesh_ca_certificate_provider_factory_test.cc"], + external_deps = ["gtest"], + language = "C++", + deps = [ + "//:gpr", + "//:grpc", + "//test/core/util:grpc_test_util", + ], +) diff --git a/test/core/xds/google_mesh_ca_certificate_provider_factory_test.cc b/test/core/xds/google_mesh_ca_certificate_provider_factory_test.cc new file mode 100644 index 00000000000..8d336c3d3e9 --- /dev/null +++ b/test/core/xds/google_mesh_ca_certificate_provider_factory_test.cc @@ -0,0 +1,367 @@ +// +// +// 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 + +#include + +#include "src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h" +#include "test/core/util/test_config.h" + +namespace grpc_core { +namespace testing { +namespace { + +TEST(GoogleMeshCaConfigTest, Basic) { + const char* json_str = + "{" + " \"server\": {" + " \"api_type\": \"GRPC\"," + " \"grpc_services\": [{" + " \"google_grpc\": {" + " \"target_uri\": \"newmeshca.googleapis.com\"," + " \"channel_credentials\": { \"google_default\": {}}," + " \"call_credentials\": [{" + " \"sts_service\": {" + " \"token_exchange_service_uri\": " + "\"newsecuretoken.googleapis.com\"," + " \"resource\": \"newmeshca.googleapis.com\"," + " \"audience\": \"newmeshca.googleapis.com\"," + " \"scope\": " + "\"https://www.newgoogleapis.com/auth/cloud-platform\"," + " \"requested_token_type\": " + "\"urn:ietf:params:oauth:token-type:jwt\"," + " \"subject_token_path\": \"/etc/secret/sajwt.token\"," + " \"subject_token_type\": " + "\"urn:ietf:params:oauth:token-type:jwt\"," + " \"actor_token_path\": \"/etc/secret/sajwt.token\"," + " \"actor_token_type\": " + "\"urn:ietf:params:oauth:token-type:jwt\"" + " }" + " }]" + " }," + " \"timeout\": \"20s\"" + " }]" + " }," + " \"certificate_lifetime\": \"400s\"," + " \"renewal_grace_period\": \"100s\"," + " \"key_type\": \"RSA\"," + " \"key_size\": 1024," + " \"location\": " + "\"https://container.googleapis.com/v1/project/test-project1/locations/" + "test-zone2/clusters/test-cluster3\"" + "}"; + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + GoogleMeshCaCertificateProviderFactory::Config::Parse(json, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + EXPECT_EQ(config->endpoint(), "newmeshca.googleapis.com"); + EXPECT_EQ(config->sts_config().token_exchange_service_uri, + "newsecuretoken.googleapis.com"); + EXPECT_EQ(config->sts_config().resource, "newmeshca.googleapis.com"); + EXPECT_EQ(config->sts_config().audience, "newmeshca.googleapis.com"); + EXPECT_EQ(config->sts_config().scope, + "https://www.newgoogleapis.com/auth/cloud-platform"); + EXPECT_EQ(config->sts_config().requested_token_type, + "urn:ietf:params:oauth:token-type:jwt"); + EXPECT_EQ(config->sts_config().subject_token_path, "/etc/secret/sajwt.token"); + EXPECT_EQ(config->sts_config().subject_token_type, + "urn:ietf:params:oauth:token-type:jwt"); + EXPECT_EQ(config->sts_config().actor_token_path, "/etc/secret/sajwt.token"); + EXPECT_EQ(config->sts_config().actor_token_type, + "urn:ietf:params:oauth:token-type:jwt"); + EXPECT_EQ(config->timeout(), 20 * 1000); + EXPECT_EQ(config->certificate_lifetime(), 400 * 1000); + EXPECT_EQ(config->renewal_grace_period(), 100 * 1000); + EXPECT_EQ(config->key_size(), 1024); + EXPECT_EQ(config->location(), + "https://container.googleapis.com/v1/project/test-project1/" + "locations/test-zone2/clusters/test-cluster3"); +} + +TEST(GoogleMeshCaConfigTest, Defaults) { + const char* json_str = + "{" + " \"server\": {" + " \"api_type\": \"GRPC\"," + " \"grpc_services\": [{" + " \"google_grpc\": {" + " \"call_credentials\": [{" + " \"sts_service\": {" + " \"scope\": " + "\"https://www.googleapis.com/auth/cloud-platform\"," + " \"subject_token_path\": \"/etc/secret/sajwt.token\"," + " \"subject_token_type\": " + "\"urn:ietf:params:oauth:token-type:jwt\"" + " }" + " }]" + " }" + " }]" + " }," + " \"location\": " + "\"https://container.googleapis.com/v1/project/test-project1/locations/" + "test-zone2/clusters/test-cluster3\"" + "}"; + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + GoogleMeshCaCertificateProviderFactory::Config::Parse(json, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + EXPECT_EQ(config->endpoint(), "meshca.googleapis.com"); + EXPECT_EQ(config->sts_config().token_exchange_service_uri, + "securetoken.googleapis.com"); + EXPECT_EQ(config->sts_config().resource, ""); + EXPECT_EQ(config->sts_config().audience, ""); + EXPECT_EQ(config->sts_config().scope, + "https://www.googleapis.com/auth/cloud-platform"); + EXPECT_EQ(config->sts_config().requested_token_type, ""); + EXPECT_EQ(config->sts_config().subject_token_path, "/etc/secret/sajwt.token"); + EXPECT_EQ(config->sts_config().subject_token_type, + "urn:ietf:params:oauth:token-type:jwt"); + EXPECT_EQ(config->sts_config().actor_token_path, ""); + EXPECT_EQ(config->sts_config().actor_token_type, ""); + EXPECT_EQ(config->timeout(), 10 * 1000); + EXPECT_EQ(config->certificate_lifetime(), 24 * 60 * 60 * 1000); + EXPECT_EQ(config->renewal_grace_period(), 12 * 60 * 60 * 1000); + EXPECT_EQ(config->key_size(), 2048); + EXPECT_EQ(config->location(), + "https://container.googleapis.com/v1/project/test-project1/" + "locations/test-zone2/clusters/test-cluster3"); +} + +TEST(GoogleMeshCaConfigTest, WrongExpectedValues) { + const char* json_str = + "{" + " \"server\": {" + " \"api_type\": \"REST\"," + " \"grpc_services\": [{" + " \"google_grpc\": {" + " \"call_credentials\": [{" + " \"sts_service\": {" + " \"scope\": " + "\"https://www.googleapis.com/auth/cloud-platform\"," + " \"subject_token_path\": \"/etc/secret/sajwt.token\"," + " \"subject_token_type\": " + "\"urn:ietf:params:oauth:token-type:jwt\"" + " }" + " }]" + " }" + " }]" + " }," + " \"key_type\": \"DSA\"," + " \"location\": " + "\"https://container.googleapis.com/v1/project/test-project1/locations/" + "test-zone2/clusters/test-cluster3\"" + "}"; + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + GoogleMeshCaCertificateProviderFactory::Config::Parse(json, &error); + EXPECT_THAT( + grpc_error_string(error), + ::testing::ContainsRegex("field:api_type error:Only GRPC is supported.*" + "field:key_type error:Only RSA is supported")); + GRPC_ERROR_UNREF(error); +} + +TEST(GoogleMeshCaConfigTest, WrongTypes) { + const char* json_str = + "{" + " \"server\": {" + " \"api_type\": 123," + " \"grpc_services\": [{" + " \"google_grpc\": {" + " \"target_uri\": 123," + " \"call_credentials\": [{" + " \"sts_service\": {" + " \"token_exchange_service_uri\": 123," + " \"resource\": 123," + " \"audience\": 123," + " \"scope\": 123," + " \"requested_token_type\": 123," + " \"subject_token_path\": 123," + " \"subject_token_type\": 123," + " \"actor_token_path\": 123," + " \"actor_token_type\": 123" + " }" + " }]" + " }," + " \"timeout\": 20" + " }]" + " }," + " \"certificate_lifetime\": 400," + " \"renewal_grace_period\": 100," + " \"key_type\": 123," + " \"key_size\": \"1024\"," + " \"location\": 123" + "}"; + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + GoogleMeshCaCertificateProviderFactory::Config::Parse(json, &error); + EXPECT_THAT( + grpc_error_string(error), + ::testing::ContainsRegex( + "field:server.*field:api_type error:type should be STRING.*" + "field:grpc_services.*field:google_grpc.*field:target_uri " + "error:type should be STRING.*" + "field:call_credentials.*field:sts_service.*field:token_exchange_" + "service_uri error:type should be STRING.*" + "field:resource error:type should be STRING.*" + "field:audience error:type should be STRING.*" + "field:scope error:type should be STRING.*" + "field:requested_token_type error:type should be STRING.*" + "field:subject_token_path error:type should be STRING.*" + "field:subject_token_type error:type should be STRING.*" + "field:actor_token_path error:type should be STRING.*" + "field:actor_token_type error:type should be STRING.*" + "field:timeout error:type should be STRING of the form given by " + "google.proto.Duration.*" + "field:certificate_lifetime error:type should be STRING of the form " + "given by google.proto.Duration.*" + "field:renewal_grace_period error:type should be STRING of the form " + "given by google.proto.Duration..*" + "field:key_type error:type should be STRING.*" + "field:key_size error:type should be NUMBER.*" + "field:location error:type should be STRING")); + GRPC_ERROR_UNREF(error); +} + +TEST(GoogleMeshCaConfigTest, GrpcServicesNotAnArray) { + const char* json_str = + "{" + " \"server\": {" + " \"api_type\": \"GRPC\"," + " \"grpc_services\": 123" + " }," + " \"location\": " + "\"https://container.googleapis.com/v1/project/test-project1/locations/" + "test-zone2/clusters/test-cluster3\"" + "}"; + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + GoogleMeshCaCertificateProviderFactory::Config::Parse(json, &error); + EXPECT_THAT( + grpc_error_string(error), + ::testing::ContainsRegex( + "field:server.*field:grpc_services error:type should be ARRAY")); + GRPC_ERROR_UNREF(error); +} + +TEST(GoogleMeshCaConfigTest, GoogleGrpcNotAnObject) { + const char* json_str = + "{" + " \"server\": {" + " \"api_type\": \"GRPC\"," + " \"grpc_services\": [{" + " \"google_grpc\": 123" + " }]" + " }," + " \"location\": " + "\"https://container.googleapis.com/v1/project/test-project1/locations/" + "test-zone2/clusters/test-cluster3\"" + "}"; + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + GoogleMeshCaCertificateProviderFactory::Config::Parse(json, &error); + EXPECT_THAT( + grpc_error_string(error), + ::testing::ContainsRegex("field:server.*field:grpc_services.*field:" + "google_grpc error:type should be OBJECT")); + GRPC_ERROR_UNREF(error); +} + +TEST(GoogleMeshCaConfigTest, CallCredentialsNotAnArray) { + const char* json_str = + "{" + " \"server\": {" + " \"api_type\": \"GRPC\"," + " \"grpc_services\": [{" + " \"google_grpc\": {" + " \"call_credentials\": 123" + " }" + " }]" + " }," + " \"location\": " + "\"https://container.googleapis.com/v1/project/test-project1/locations/" + "test-zone2/clusters/test-cluster3\"" + "}"; + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + GoogleMeshCaCertificateProviderFactory::Config::Parse(json, &error); + EXPECT_THAT(grpc_error_string(error), + ::testing::ContainsRegex( + "field:server.*field:grpc_services.*field:google_grpc.*" + "field:call_credentials error:type should be ARRAY")); + GRPC_ERROR_UNREF(error); +} + +TEST(GoogleMeshCaConfigTest, StsServiceNotAnObject) { + const char* json_str = + "{" + " \"server\": {" + " \"api_type\": \"GRPC\"," + " \"grpc_services\": [{" + " \"google_grpc\": {" + " \"call_credentials\": [{" + " \"sts_service\": 123" + " }]" + " }" + " }]" + " }," + " \"location\": " + "\"https://container.googleapis.com/v1/project/test-project1/locations/" + "test-zone2/clusters/test-cluster3\"" + "}"; + grpc_error* error = GRPC_ERROR_NONE; + Json json = Json::Parse(json_str, &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + auto config = + GoogleMeshCaCertificateProviderFactory::Config::Parse(json, &error); + EXPECT_THAT( + grpc_error_string(error), + ::testing::ContainsRegex( + "field:server.*field:grpc_services.*field:google_grpc.*field:" + "call_credentials.*field:sts_service error:type should be OBJECT")); + GRPC_ERROR_UNREF(error); +} + +} // namespace +} // namespace testing +} // namespace grpc_core + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + grpc::testing::TestEnvironment env(argc, argv); + grpc_init(); + auto result = RUN_ALL_TESTS(); + grpc_shutdown(); + return result; +} diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index da554e3bd73..b29d607492e 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -1387,6 +1387,8 @@ src/core/ext/xds/certificate_provider_factory.h \ src/core/ext/xds/certificate_provider_registry.cc \ src/core/ext/xds/certificate_provider_registry.h \ src/core/ext/xds/certificate_provider_store.h \ +src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc \ +src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h \ src/core/ext/xds/xds_api.cc \ src/core/ext/xds/xds_api.h \ src/core/ext/xds/xds_bootstrap.cc \ @@ -1696,6 +1698,8 @@ src/core/lib/iomgr/work_serializer.cc \ src/core/lib/iomgr/work_serializer.h \ src/core/lib/json/json.h \ src/core/lib/json/json_reader.cc \ +src/core/lib/json/json_util.cc \ +src/core/lib/json/json_util.h \ src/core/lib/json/json_writer.cc \ src/core/lib/profiling/basic_timers.cc \ src/core/lib/profiling/stap_timers.cc \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 7eb5f8d9434..7812de9c5a0 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1222,6 +1222,8 @@ src/core/ext/xds/certificate_provider_factory.h \ src/core/ext/xds/certificate_provider_registry.cc \ src/core/ext/xds/certificate_provider_registry.h \ src/core/ext/xds/certificate_provider_store.h \ +src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc \ +src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h \ src/core/ext/xds/xds_api.cc \ src/core/ext/xds/xds_api.h \ src/core/ext/xds/xds_bootstrap.cc \ @@ -1536,6 +1538,8 @@ src/core/lib/iomgr/work_serializer.cc \ src/core/lib/iomgr/work_serializer.h \ src/core/lib/json/json.h \ src/core/lib/json/json_reader.cc \ +src/core/lib/json/json_util.cc \ +src/core/lib/json/json_util.h \ src/core/lib/json/json_writer.cc \ src/core/lib/profiling/basic_timers.cc \ src/core/lib/profiling/stap_timers.cc \ diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 67d7e8aca6c..de8acb8ca5d 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -4503,6 +4503,30 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": true, + "language": "c++", + "name": "google_mesh_ca_certificate_provider_factory_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "uses_polling": true + }, { "args": [], "benchmark": false,