Imported Upstream version 1.33.1
[platform/upstream/grpc.git] / src / core / ext / xds / google_mesh_ca_certificate_provider_factory.cc
1 //
2 //
3 // Copyright 2020 gRPC authors.
4 //
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
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
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.
16 //
17 //
18
19 #include <grpc/support/port_platform.h>
20
21 #include "src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h"
22
23 #include <sstream>
24 #include <type_traits>
25
26 #include "absl/strings/str_cat.h"
27
28 #include <grpc/support/string_util.h>
29
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"
33
34 namespace grpc_core {
35
36 namespace {
37
38 const char* kMeshCaPlugin = "meshCA";
39
40 //
41 // Helper functions for extracting types from JSON
42 //
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")
50             .c_str()));
51     return false;
52   }
53   std::istringstream ss(json.string_value());
54   ss >> *output;
55   // The JSON parsing API should have dealt with parsing errors, but check
56   // anyway
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()));
60     return false;
61   }
62   return true;
63 }
64
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:
70       *output = true;
71       return true;
72     case Json::Type::JSON_FALSE:
73       *output = false;
74       return true;
75     default:
76       error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
77           absl::StrCat("field:", field_name, " error:type should be BOOLEAN")
78               .c_str()));
79       return false;
80   }
81 }
82
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) {
87     *output = "";
88     error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
89         absl::StrCat("field:", field_name, " error:type should be STRING")
90             .c_str()));
91     return false;
92   }
93   *output = json.string_value();
94   return true;
95 }
96
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) {
101     *output = nullptr;
102     error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
103         absl::StrCat("field:", field_name, " error:type should be ARRAY")
104             .c_str()));
105     return false;
106   }
107   *output = &json.array_value();
108   return true;
109 }
110
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) {
115     *output = nullptr;
116     error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
117         absl::StrCat("field:", field_name, " error:type should be OBJECT")
118             .c_str()));
119     return false;
120   }
121   *output = &json.object_value();
122   return true;
123 }
124
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.")
134             .c_str()));
135     return false;
136   }
137   return true;
138 }
139
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()) {
146     if (!optional) {
147       error_list->push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(
148           absl::StrCat("field:", field_name, " error:does not exist.")
149               .c_str()));
150     }
151     return false;
152   }
153   auto& child_object_json = it->second;
154   return ExtractJsonType(child_object_json, field_name, output, error_list);
155 }
156
157 }  // namespace
158
159 //
160 // GoogleMeshCaCertificateProviderFactory::Config
161 //
162
163 const char* GoogleMeshCaCertificateProviderFactory::Config::name() const {
164   return kMeshCaPlugin;
165 }
166
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
176   }
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)) {
183     sts_config_.scope =
184         "https://www.googleapis.com/auth/cloud-platform";  // default
185   }
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,
197                        true);
198   ParseJsonObjectField(sts_service, "actor_token_type",
199                        &sts_config_.actor_token_type, &error_list_sts_service,
200                        true);
201   return error_list_sts_service;
202 }
203
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));
216     }
217   }
218   return error_list_call_credentials;
219 }
220
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
228   }
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."));
235     } else {
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));
244         }
245       }
246     }
247   }
248
249   return error_list_google_grpc;
250 }
251
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));
264     }
265   }
266   if (!ParseJsonObjectField(grpc_service, "timeout", &timeout_,
267                             &error_list_grpc_services, true)) {
268     timeout_ = 10 * 1000;  // 10sec default
269   }
270   return error_list_grpc_services;
271 }
272
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,
279                            true)) {
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"));
283     }
284   }
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"));
291     } else {
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));
300         }
301       }
302     }
303   }
304   return error_list_server;
305 }
306
307 std::unique_ptr<GoogleMeshCaCertificateProviderFactory::Config>
308 GoogleMeshCaCertificateProviderFactory::Config::Parse(const Json& config_json,
309                                                       grpc_error** error) {
310   auto config =
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.");
315     return nullptr;
316   }
317   std::vector<grpc_error*> error_list;
318   const Json::Object* server = nullptr;
319   if (ParseJsonObjectField(config_json.object_value(), "server", &server,
320                            &error_list)) {
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));
326     }
327   }
328   if (!ParseJsonObjectField(config_json.object_value(), "certificate_lifetime",
329                             &config->certificate_lifetime_, &error_list,
330                             true)) {
331     config->certificate_lifetime_ = 24 * 60 * 60 * 1000;  // 24hrs default
332   }
333   if (!ParseJsonObjectField(config_json.object_value(), "renewal_grace_period",
334                             &config->renewal_grace_period_, &error_list,
335                             true)) {
336     config->renewal_grace_period_ = 12 * 60 * 60 * 1000;  // 12hrs default
337   }
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."));
344     }
345   }
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
349   }
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.
353   }
354   if (!error_list.empty()) {
355     *error = GRPC_ERROR_CREATE_FROM_VECTOR(
356         "Error parsing google Mesh CA config", &error_list);
357     return nullptr;
358   }
359   return config;
360 }
361
362 //
363 // GoogleMeshCaCertificateProviderFactory
364 //
365
366 const char* GoogleMeshCaCertificateProviderFactory::name() const {
367   return kMeshCaPlugin;
368 }
369
370 std::unique_ptr<CertificateProviderFactory::Config>
371 GoogleMeshCaCertificateProviderFactory::CreateCertificateProviderConfig(
372     const Json& config_json, grpc_error** error) {
373   return GoogleMeshCaCertificateProviderFactory::Config::Parse(config_json,
374                                                                error);
375 }
376
377 }  // namespace grpc_core