3 // Copyright 2020 gRPC authors.
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
19 #include <grpc/support/port_platform.h>
21 #include "src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h"
24 #include <type_traits>
26 #include "absl/strings/str_cat.h"
28 #include <grpc/support/string_util.h>
30 #include "src/core/lib/gpr/string.h"
31 #include "src/core/lib/iomgr/error.h"
32 #include "src/core/lib/json/json_util.h"
38 const char* kMeshCaPlugin = "meshCA";
41 // Helper functions for extracting types from JSON
43 template <typename NumericType, typename ErrorVectorType>
44 bool ExtractJsonType(const Json& json, const std::string& field_name,
45 NumericType* output, ErrorVectorType* error_list) {
46 static_assert(std::is_integral<NumericType>::value, "Integral required");
47 if (json.type() != Json::Type::NUMBER) {
48 error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
49 absl::StrCat("field:", field_name, " error:type should be NUMBER")
53 std::istringstream ss(json.string_value());
55 // The JSON parsing API should have dealt with parsing errors, but check
57 if (GPR_UNLIKELY(ss.bad())) {
58 error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
59 absl::StrCat("field:", field_name, " error:failed to parse.").c_str()));
65 template <typename ErrorVectorType>
66 bool ExtractJsonType(const Json& json, const std::string& field_name,
67 bool* output, ErrorVectorType* error_list) {
68 switch (json.type()) {
69 case Json::Type::JSON_TRUE:
72 case Json::Type::JSON_FALSE:
76 error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
77 absl::StrCat("field:", field_name, " error:type should be BOOLEAN")
83 template <typename ErrorVectorType>
84 bool ExtractJsonType(const Json& json, const std::string& field_name,
85 std::string* output, ErrorVectorType* error_list) {
86 if (json.type() != Json::Type::STRING) {
88 error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
89 absl::StrCat("field:", field_name, " error:type should be STRING")
93 *output = json.string_value();
97 template <typename ErrorVectorType>
98 bool ExtractJsonType(const Json& json, const std::string& field_name,
99 const Json::Array** output, ErrorVectorType* error_list) {
100 if (json.type() != Json::Type::ARRAY) {
102 error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
103 absl::StrCat("field:", field_name, " error:type should be ARRAY")
107 *output = &json.array_value();
111 template <typename ErrorVectorType>
112 bool ExtractJsonType(const Json& json, const std::string& field_name,
113 const Json::Object** output, ErrorVectorType* error_list) {
114 if (json.type() != Json::Type::OBJECT) {
116 error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
117 absl::StrCat("field:", field_name, " error:type should be OBJECT")
121 *output = &json.object_value();
125 template <typename ErrorVectorType>
126 bool ExtractJsonType(const Json& json, const std::string& field_name,
127 grpc_millis* output, ErrorVectorType* error_list) {
128 if (!ParseDurationFromJson(json, output)) {
129 *output = GRPC_MILLIS_INF_PAST;
130 error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
131 absl::StrCat("field:", field_name,
132 " error:type should be STRING of the form given by "
133 "google.proto.Duration.")
140 template <typename T, typename ErrorVectorType>
141 bool ParseJsonObjectField(const Json::Object& object,
142 const std::string& field_name, T* output,
143 ErrorVectorType* error_list, bool optional = false) {
144 auto it = object.find(field_name);
145 if (it == object.end()) {
147 error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
148 absl::StrCat("field:", field_name, " error:does not exist.")
153 auto& child_object_json = it->second;
154 return ExtractJsonType(child_object_json, field_name, output, error_list);
160 // GoogleMeshCaCertificateProviderFactory::Config
163 const char* GoogleMeshCaCertificateProviderFactory::Config::name() const {
164 return kMeshCaPlugin;
167 std::vector<grpc_error*>
168 GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectStsService(
169 const Json::Object& sts_service) {
170 std::vector<grpc_error*> error_list_sts_service;
171 if (!ParseJsonObjectField(sts_service, "token_exchange_service_uri",
172 &sts_config_.token_exchange_service_uri,
173 &error_list_sts_service, true)) {
174 sts_config_.token_exchange_service_uri =
175 "securetoken.googleapis.com"; // default
177 ParseJsonObjectField(sts_service, "resource", &sts_config_.resource,
178 &error_list_sts_service, true);
179 ParseJsonObjectField(sts_service, "audience", &sts_config_.audience,
180 &error_list_sts_service, true);
181 if (!ParseJsonObjectField(sts_service, "scope", &sts_config_.scope,
182 &error_list_sts_service, true)) {
184 "https://www.googleapis.com/auth/cloud-platform"; // default
186 ParseJsonObjectField(sts_service, "requested_token_type",
187 &sts_config_.requested_token_type,
188 &error_list_sts_service, true);
189 ParseJsonObjectField(sts_service, "subject_token_path",
190 &sts_config_.subject_token_path,
191 &error_list_sts_service);
192 ParseJsonObjectField(sts_service, "subject_token_type",
193 &sts_config_.subject_token_type,
194 &error_list_sts_service);
195 ParseJsonObjectField(sts_service, "actor_token_path",
196 &sts_config_.actor_token_path, &error_list_sts_service,
198 ParseJsonObjectField(sts_service, "actor_token_type",
199 &sts_config_.actor_token_type, &error_list_sts_service,
201 return error_list_sts_service;
204 std::vector<grpc_error*>
205 GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectCallCredentials(
206 const Json::Object& call_credentials) {
207 std::vector<grpc_error*> error_list_call_credentials;
208 const Json::Object* sts_service = nullptr;
209 if (ParseJsonObjectField(call_credentials, "sts_service", &sts_service,
210 &error_list_call_credentials)) {
211 std::vector<grpc_error*> error_list_sts_service =
212 ParseJsonObjectStsService(*sts_service);
213 if (!error_list_sts_service.empty()) {
214 error_list_call_credentials.push_back(GRPC_ERROR_CREATE_FROM_VECTOR(
215 "field:sts_service", &error_list_sts_service));
218 return error_list_call_credentials;
221 std::vector<grpc_error*>
222 GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectGoogleGrpc(
223 const Json::Object& google_grpc) {
224 std::vector<grpc_error*> error_list_google_grpc;
225 if (!ParseJsonObjectField(google_grpc, "target_uri", &endpoint_,
226 &error_list_google_grpc, true)) {
227 endpoint_ = "meshca.googleapis.com"; // Default target
229 const Json::Array* call_credentials_array = nullptr;
230 if (ParseJsonObjectField(google_grpc, "call_credentials",
231 &call_credentials_array, &error_list_google_grpc)) {
232 if (call_credentials_array->size() != 1) {
233 error_list_google_grpc.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
234 "field:call_credentials error:Need exactly one entry."));
236 const Json::Object* call_credentials = nullptr;
237 if (ExtractJsonType((*call_credentials_array)[0], "call_credentials[0]",
238 &call_credentials, &error_list_google_grpc)) {
239 std::vector<grpc_error*> error_list_call_credentials =
240 ParseJsonObjectCallCredentials(*call_credentials);
241 if (!error_list_call_credentials.empty()) {
242 error_list_google_grpc.push_back(GRPC_ERROR_CREATE_FROM_VECTOR(
243 "field:call_credentials", &error_list_call_credentials));
249 return error_list_google_grpc;
252 std::vector<grpc_error*>
253 GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectGrpcServices(
254 const Json::Object& grpc_service) {
255 std::vector<grpc_error*> error_list_grpc_services;
256 const Json::Object* google_grpc = nullptr;
257 if (ParseJsonObjectField(grpc_service, "google_grpc", &google_grpc,
258 &error_list_grpc_services)) {
259 std::vector<grpc_error*> error_list_google_grpc =
260 ParseJsonObjectGoogleGrpc(*google_grpc);
261 if (!error_list_google_grpc.empty()) {
262 error_list_grpc_services.push_back(GRPC_ERROR_CREATE_FROM_VECTOR(
263 "field:google_grpc", &error_list_google_grpc));
266 if (!ParseJsonObjectField(grpc_service, "timeout", &timeout_,
267 &error_list_grpc_services, true)) {
268 timeout_ = 10 * 1000; // 10sec default
270 return error_list_grpc_services;
273 std::vector<grpc_error*>
274 GoogleMeshCaCertificateProviderFactory::Config::ParseJsonObjectServer(
275 const Json::Object& server) {
276 std::vector<grpc_error*> error_list_server;
277 std::string api_type;
278 if (ParseJsonObjectField(server, "api_type", &api_type, &error_list_server,
280 if (api_type != "GRPC") {
281 error_list_server.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
282 "field:api_type error:Only GRPC is supported"));
285 const Json::Array* grpc_services = nullptr;
286 if (ParseJsonObjectField(server, "grpc_services", &grpc_services,
287 &error_list_server)) {
288 if (grpc_services->size() != 1) {
289 error_list_server.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
290 "field:grpc_services error:Need exactly one entry"));
292 const Json::Object* grpc_service = nullptr;
293 if (ExtractJsonType((*grpc_services)[0], "grpc_services[0]",
294 &grpc_service, &error_list_server)) {
295 std::vector<grpc_error*> error_list_grpc_services =
296 ParseJsonObjectGrpcServices(*grpc_service);
297 if (!error_list_grpc_services.empty()) {
298 error_list_server.push_back(GRPC_ERROR_CREATE_FROM_VECTOR(
299 "field:grpc_services", &error_list_grpc_services));
304 return error_list_server;
307 std::unique_ptr<GoogleMeshCaCertificateProviderFactory::Config>
308 GoogleMeshCaCertificateProviderFactory::Config::Parse(const Json& config_json,
309 grpc_error** error) {
311 absl::make_unique<GoogleMeshCaCertificateProviderFactory::Config>();
312 if (config_json.type() != Json::Type::OBJECT) {
313 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
314 "error:config type should be OBJECT.");
317 std::vector<grpc_error*> error_list;
318 const Json::Object* server = nullptr;
319 if (ParseJsonObjectField(config_json.object_value(), "server", &server,
321 std::vector<grpc_error*> error_list_server =
322 config->ParseJsonObjectServer(*server);
323 if (!error_list_server.empty()) {
324 error_list.push_back(
325 GRPC_ERROR_CREATE_FROM_VECTOR("field:server", &error_list_server));
328 if (!ParseJsonObjectField(config_json.object_value(), "certificate_lifetime",
329 &config->certificate_lifetime_, &error_list,
331 config->certificate_lifetime_ = 24 * 60 * 60 * 1000; // 24hrs default
333 if (!ParseJsonObjectField(config_json.object_value(), "renewal_grace_period",
334 &config->renewal_grace_period_, &error_list,
336 config->renewal_grace_period_ = 12 * 60 * 60 * 1000; // 12hrs default
338 std::string key_type;
339 if (ParseJsonObjectField(config_json.object_value(), "key_type", &key_type,
340 &error_list, true)) {
341 if (key_type != "RSA") {
342 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
343 "field:key_type error:Only RSA is supported."));
346 if (!ParseJsonObjectField(config_json.object_value(), "key_size",
347 &config->key_size_, &error_list, true)) {
348 config->key_size_ = 2048; // default 2048 bit key size
350 if (!ParseJsonObjectField(config_json.object_value(), "location",
351 &config->location_, &error_list, true)) {
352 // GCE/GKE Metadata server needs to be contacted to get the value.
354 if (!error_list.empty()) {
355 *error = GRPC_ERROR_CREATE_FROM_VECTOR(
356 "Error parsing google Mesh CA config", &error_list);
363 // GoogleMeshCaCertificateProviderFactory
366 const char* GoogleMeshCaCertificateProviderFactory::name() const {
367 return kMeshCaPlugin;
370 std::unique_ptr<CertificateProviderFactory::Config>
371 GoogleMeshCaCertificateProviderFactory::CreateCertificateProviderConfig(
372 const Json& config_json, grpc_error** error) {
373 return GoogleMeshCaCertificateProviderFactory::Config::Parse(config_json,
377 } // namespace grpc_core