mirror of https://github.com/grpc/grpc.git
Merge pull request #24127 from yashykt/meshcaconfig
Add parsing logic for GoogleMeshCaConfigpull/24173/head
commit
3ee45eceef
24 changed files with 1124 additions and 36 deletions
@ -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 <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h" |
||||
|
||||
#include <sstream> |
||||
#include <type_traits> |
||||
|
||||
#include "absl/strings/str_cat.h" |
||||
|
||||
#include <grpc/support/string_util.h> |
||||
|
||||
#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 <typename NumericType, typename ErrorVectorType> |
||||
bool ExtractJsonType(const Json& json, const std::string& field_name, |
||||
NumericType* output, ErrorVectorType* error_list) { |
||||
static_assert(std::is_integral<NumericType>::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 <typename ErrorVectorType> |
||||
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 <typename ErrorVectorType> |
||||
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 <typename ErrorVectorType> |
||||
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 <typename ErrorVectorType> |
||||
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 <typename ErrorVectorType> |
||||
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 <typename T, typename ErrorVectorType> |
||||
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<grpc_error*> |
||||
GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectStsService( |
||||
const Json::Object& sts_service) { |
||||
std::vector<grpc_error*> 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<grpc_error*> |
||||
GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectCallCredentials( |
||||
const Json::Object& call_credentials) { |
||||
std::vector<grpc_error*> error_list_call_credentials; |
||||
const Json::Object* sts_service = nullptr; |
||||
if (ParseJsonObjectField(call_credentials, "sts_service", &sts_service, |
||||
&error_list_call_credentials)) { |
||||
std::vector<grpc_error*> 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<grpc_error*> |
||||
GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectGoogleGrpc( |
||||
const Json::Object& google_grpc) { |
||||
std::vector<grpc_error*> 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<grpc_error*> 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<grpc_error*> |
||||
GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectGrpcServices( |
||||
const Json::Object& grpc_service) { |
||||
std::vector<grpc_error*> error_list_grpc_services; |
||||
const Json::Object* google_grpc = nullptr; |
||||
if (ParseJsonObjectField(grpc_service, "google_grpc", &google_grpc, |
||||
&error_list_grpc_services)) { |
||||
std::vector<grpc_error*> 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<grpc_error*> |
||||
GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectServer( |
||||
const Json::Object& server) { |
||||
std::vector<grpc_error*> 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<grpc_error*> 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> |
||||
GoogleMeshCaCertificateProviderFactory::Config::Parse(const Json& config_json, |
||||
grpc_error** error) { |
||||
auto config = |
||||
absl::make_unique<GoogleMeshCaCertificateProviderFactory::Config>(); |
||||
if (config_json.type() != Json::Type::OBJECT) { |
||||
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
||||
"error:config type should be OBJECT."); |
||||
return nullptr; |
||||
} |
||||
std::vector<grpc_error*> error_list; |
||||
const Json::Object* server = nullptr; |
||||
if (ParseJsonObjectField(config_json.object_value(), "server", &server, |
||||
&error_list)) { |
||||
std::vector<grpc_error*> 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<CertificateProviderFactory::Config> |
||||
GoogleMeshCaCertificateProviderFactory::CreateCertificateProviderConfig( |
||||
const Json& config_json, grpc_error** error) { |
||||
return GoogleMeshCaCertificateProviderFactory::Config::Parse(config_json, |
||||
error); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -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 <grpc/support/port_platform.h> |
||||
|
||||
#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<Config> Parse(const Json& config_json, |
||||
grpc_error** error); |
||||
|
||||
private: |
||||
// Helpers for parsing the config
|
||||
std::vector<grpc_error*> ParseJsonObjectStsService( |
||||
const Json::Object& sts_service); |
||||
std::vector<grpc_error*> ParseJsonObjectCallCredentials( |
||||
const Json::Object& call_credentials); |
||||
std::vector<grpc_error*> ParseJsonObjectGoogleGrpc( |
||||
const Json::Object& google_grpc); |
||||
std::vector<grpc_error*> ParseJsonObjectGrpcServices( |
||||
const Json::Object& grpc_service); |
||||
std::vector<grpc_error*> 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<CertificateProviderFactory::Config> |
||||
CreateCertificateProviderConfig(const Json& config_json, |
||||
grpc_error** error) override; |
||||
|
||||
RefCountedPtr<grpc_tls_certificate_provider> CreateCertificateProvider( |
||||
std::unique_ptr<CertificateProviderFactory::Config> config) override { |
||||
// TODO(yashykt) : To be implemented
|
||||
return nullptr; |
||||
} |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_CORE_EXT_XDS_GOOGLE_MESH_CA_CERTIFICATE_PROVIDER_FACTORY_H
|
@ -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 <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/json/json_util.h" |
||||
|
||||
#include <grpc/support/string_util.h> |
||||
|
||||
#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<char> 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<int>(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
|
@ -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 <grpc/support/port_platform.h> |
||||
|
||||
#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
|
@ -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", |
||||
], |
||||
) |
@ -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 <gmock/gmock.h> |
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <grpc/grpc.h> |
||||
|
||||
#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; |
||||
} |
Loading…
Reference in new issue