Imported Upstream version 1.41.0
[platform/upstream/grpc.git] / src / core / lib / security / authorization / rbac_translator.cc
1 // Copyright 2021 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <grpc/support/port_platform.h>
16
17 #include "src/core/lib/security/authorization/rbac_translator.h"
18
19 #include "absl/strings/str_cat.h"
20 #include "absl/strings/str_format.h"
21 #include "absl/strings/strip.h"
22
23 #include "src/core/lib/gpr/useful.h"
24 #include "src/core/lib/matchers/matchers.h"
25
26 namespace grpc_core {
27
28 namespace {
29
30 absl::string_view GetMatcherType(absl::string_view value,
31                                  StringMatcher::Type* type) {
32   if (value == "*") {
33     *type = StringMatcher::Type::kPrefix;
34     return "";
35   } else if (absl::StartsWith(value, "*")) {
36     *type = StringMatcher::Type::kSuffix;
37     return absl::StripPrefix(value, "*");
38   } else if (absl::EndsWith(value, "*")) {
39     *type = StringMatcher::Type::kPrefix;
40     return absl::StripSuffix(value, "*");
41   }
42   *type = StringMatcher::Type::kExact;
43   return value;
44 }
45
46 absl::StatusOr<StringMatcher> GetStringMatcher(absl::string_view value) {
47   StringMatcher::Type type;
48   absl::string_view matcher = GetMatcherType(value, &type);
49   return StringMatcher::Create(type, matcher);
50 }
51
52 absl::StatusOr<HeaderMatcher> GetHeaderMatcher(absl::string_view name,
53                                                absl::string_view value) {
54   StringMatcher::Type type;
55   absl::string_view matcher = GetMatcherType(value, &type);
56   return HeaderMatcher::Create(name, static_cast<HeaderMatcher::Type>(type),
57                                matcher);
58 }
59
60 bool IsUnsupportedHeader(absl::string_view header_name) {
61   static const char* const kUnsupportedHeaders[] = {"host",
62                                                     "connection",
63                                                     "keep-alive",
64                                                     "proxy-authenticate",
65                                                     "proxy-authorization",
66                                                     "te",
67                                                     "trailer",
68                                                     "transfer-encoding",
69                                                     "upgrade"};
70   for (size_t i = 0; i < GPR_ARRAY_SIZE(kUnsupportedHeaders); ++i) {
71     if (absl::EqualsIgnoreCase(header_name, kUnsupportedHeaders[i])) {
72       return true;
73     }
74   }
75   return false;
76 }
77
78 absl::StatusOr<Rbac::Principal> ParsePrincipalsArray(const Json& json) {
79   std::vector<std::unique_ptr<Rbac::Principal>> principal_names;
80   for (size_t i = 0; i < json.array_value().size(); ++i) {
81     const Json& child = json.array_value().at(i);
82     if (child.type() != Json::Type::STRING) {
83       return absl::InvalidArgumentError(
84           absl::StrCat("\"principals\" ", i, ": is not a string."));
85     }
86     auto matcher_or = GetStringMatcher(child.string_value());
87     if (!matcher_or.ok()) {
88       return absl::Status(matcher_or.status().code(),
89                           absl::StrCat("\"principals\" ", i, ": ",
90                                        matcher_or.status().message()));
91     }
92     principal_names.push_back(absl::make_unique<Rbac::Principal>(
93         Rbac::Principal::RuleType::kPrincipalName,
94         std::move(matcher_or.value())));
95   }
96   return Rbac::Principal(Rbac::Principal::RuleType::kOr,
97                          std::move(principal_names));
98 }
99
100 absl::StatusOr<Rbac::Principal> ParsePeer(const Json& json) {
101   std::vector<std::unique_ptr<Rbac::Principal>> peer;
102   auto it = json.object_value().find("principals");
103   if (it != json.object_value().end()) {
104     if (it->second.type() != Json::Type::ARRAY) {
105       return absl::InvalidArgumentError("\"principals\" is not an array.");
106     }
107     auto principal_names_or = ParsePrincipalsArray(it->second);
108     if (!principal_names_or.ok()) return principal_names_or.status();
109     if (!principal_names_or.value().principals.empty()) {
110       peer.push_back(absl::make_unique<Rbac::Principal>(
111           std::move(principal_names_or.value())));
112     }
113   }
114   if (peer.empty()) {
115     return Rbac::Principal(Rbac::Principal::RuleType::kAny);
116   }
117   return Rbac::Principal(Rbac::Principal::RuleType::kAnd, std::move(peer));
118 }
119
120 absl::StatusOr<Rbac::Permission> ParseHeaderValues(
121     const Json& json, absl::string_view header_name) {
122   if (json.array_value().empty()) {
123     return absl::InvalidArgumentError("\"values\" list is empty.");
124   }
125   std::vector<std::unique_ptr<Rbac::Permission>> values;
126   for (size_t i = 0; i < json.array_value().size(); ++i) {
127     const Json& child = json.array_value().at(i);
128     if (child.type() != Json::Type::STRING) {
129       return absl::InvalidArgumentError(
130           absl::StrCat("\"values\" ", i, ": is not a string."));
131     }
132     auto matcher_or = GetHeaderMatcher(header_name, child.string_value());
133     if (!matcher_or.ok()) {
134       return absl::Status(
135           matcher_or.status().code(),
136           absl::StrCat("\"values\" ", i, ": ", matcher_or.status().message()));
137     }
138     values.push_back(absl::make_unique<Rbac::Permission>(
139         Rbac::Permission::RuleType::kHeader, std::move(matcher_or.value())));
140   }
141   return Rbac::Permission(Rbac::Permission::RuleType::kOr, std::move(values));
142 }
143
144 absl::StatusOr<Rbac::Permission> ParseHeaders(const Json& json) {
145   auto it = json.object_value().find("key");
146   if (it == json.object_value().end()) {
147     return absl::InvalidArgumentError("\"key\" is not present.");
148   }
149   if (it->second.type() != Json::Type::STRING) {
150     return absl::InvalidArgumentError("\"key\" is not a string.");
151   }
152   absl::string_view header_name = it->second.string_value();
153   if (absl::StartsWith(header_name, ":") ||
154       absl::StartsWith(header_name, "grpc-") ||
155       IsUnsupportedHeader(header_name)) {
156     return absl::InvalidArgumentError(
157         absl::StrFormat("Unsupported \"key\" %s.", header_name));
158   }
159   it = json.object_value().find("values");
160   if (it == json.object_value().end()) {
161     return absl::InvalidArgumentError("\"values\" is not present.");
162   }
163   if (it->second.type() != Json::Type::ARRAY) {
164     return absl::InvalidArgumentError("\"values\" is not an array.");
165   }
166   return ParseHeaderValues(it->second, header_name);
167 }
168
169 absl::StatusOr<Rbac::Permission> ParseHeadersArray(const Json& json) {
170   std::vector<std::unique_ptr<Rbac::Permission>> headers;
171   for (size_t i = 0; i < json.array_value().size(); ++i) {
172     const Json& child = json.array_value().at(i);
173     if (child.type() != Json::Type::OBJECT) {
174       return absl::InvalidArgumentError(
175           absl::StrCat("\"headers\" ", i, ": is not an object."));
176     }
177     auto headers_or = ParseHeaders(child);
178     if (!headers_or.ok()) {
179       return absl::Status(
180           headers_or.status().code(),
181           absl::StrCat("\"headers\" ", i, ": ", headers_or.status().message()));
182     }
183     headers.push_back(
184         absl::make_unique<Rbac::Permission>(std::move(headers_or.value())));
185   }
186   return Rbac::Permission(Rbac::Permission::RuleType::kAnd, std::move(headers));
187 }
188
189 absl::StatusOr<Rbac::Permission> ParsePathsArray(const Json& json) {
190   std::vector<std::unique_ptr<Rbac::Permission>> paths;
191   for (size_t i = 0; i < json.array_value().size(); ++i) {
192     const Json& child = json.array_value().at(i);
193     if (child.type() != Json::Type::STRING) {
194       return absl::InvalidArgumentError(
195           absl::StrCat("\"paths\" ", i, ": is not a string."));
196     }
197     auto matcher_or = GetStringMatcher(child.string_value());
198     if (!matcher_or.ok()) {
199       return absl::Status(
200           matcher_or.status().code(),
201           absl::StrCat("\"paths\" ", i, ": ", matcher_or.status().message()));
202     }
203     paths.push_back(absl::make_unique<Rbac::Permission>(
204         Rbac::Permission::RuleType::kPath, std::move(matcher_or.value())));
205   }
206   return Rbac::Permission(Rbac::Permission::RuleType::kOr, std::move(paths));
207 }
208
209 absl::StatusOr<Rbac::Permission> ParseRequest(const Json& json) {
210   std::vector<std::unique_ptr<Rbac::Permission>> request;
211   auto it = json.object_value().find("paths");
212   if (it != json.object_value().end()) {
213     if (it->second.type() != Json::Type::ARRAY) {
214       return absl::InvalidArgumentError("\"paths\" is not an array.");
215     }
216     auto paths_or = ParsePathsArray(it->second);
217     if (!paths_or.ok()) return paths_or.status();
218     if (!paths_or.value().permissions.empty()) {
219       request.push_back(
220           absl::make_unique<Rbac::Permission>(std::move(paths_or.value())));
221     }
222   }
223   it = json.object_value().find("headers");
224   if (it != json.object_value().end()) {
225     if (it->second.type() != Json::Type::ARRAY) {
226       return absl::InvalidArgumentError("\"headers\" is not an array.");
227     }
228     auto headers_or = ParseHeadersArray(it->second);
229     if (!headers_or.ok()) return headers_or.status();
230     if (!headers_or.value().permissions.empty()) {
231       request.push_back(
232           absl::make_unique<Rbac::Permission>(std::move(headers_or.value())));
233     }
234   }
235   if (request.empty()) {
236     return Rbac::Permission(Rbac::Permission::RuleType::kAny);
237   }
238   return Rbac::Permission(Rbac::Permission::RuleType::kAnd, std::move(request));
239 }
240
241 absl::StatusOr<Rbac::Policy> ParseRules(const Json& json) {
242   Rbac::Principal principals;
243   auto it = json.object_value().find("source");
244   if (it != json.object_value().end()) {
245     if (it->second.type() != Json::Type::OBJECT) {
246       return absl::InvalidArgumentError("\"source\" is not an object.");
247     }
248     auto peer_or = ParsePeer(it->second);
249     if (!peer_or.ok()) return peer_or.status();
250     principals = std::move(peer_or.value());
251   } else {
252     principals = Rbac::Principal(Rbac::Principal::RuleType::kAny);
253   }
254   Rbac::Permission permissions;
255   it = json.object_value().find("request");
256   if (it != json.object_value().end()) {
257     if (it->second.type() != Json::Type::OBJECT) {
258       return absl::InvalidArgumentError("\"request\" is not an object.");
259     }
260     auto request_or = ParseRequest(it->second);
261     if (!request_or.ok()) return request_or.status();
262     permissions = std::move(request_or.value());
263   } else {
264     permissions = Rbac::Permission(Rbac::Permission::RuleType::kAny);
265   }
266   return Rbac::Policy(std::move(permissions), std::move(principals));
267 }
268
269 absl::StatusOr<std::map<std::string, Rbac::Policy>> ParseRulesArray(
270     const Json& json, absl::string_view name) {
271   std::map<std::string, Rbac::Policy> policies;
272   for (size_t i = 0; i < json.array_value().size(); ++i) {
273     const Json& child = json.array_value().at(i);
274     if (child.type() != Json::Type::OBJECT) {
275       return absl::InvalidArgumentError(
276           absl::StrCat("rules ", i, ": is not an object."));
277     }
278     auto it = child.object_value().find("name");
279     if (it == child.object_value().end()) {
280       return absl::InvalidArgumentError(
281           absl::StrCat("rules ", i, ": \"name\" is not present."));
282     }
283     if (it->second.type() != Json::Type::STRING) {
284       return absl::InvalidArgumentError(
285           absl::StrCat("rules ", i, ": \"name\" is not a string."));
286     }
287     std::string policy_name =
288         std::string(name) + "_" + it->second.string_value();
289     auto policy_or = ParseRules(child);
290     if (!policy_or.ok()) {
291       return absl::Status(
292           policy_or.status().code(),
293           absl::StrCat("rules ", i, ": ", policy_or.status().message()));
294     }
295     policies[policy_name] = std::move(policy_or.value());
296   }
297   return std::move(policies);
298 }
299
300 absl::StatusOr<Rbac> ParseDenyRulesArray(const Json& json,
301                                          absl::string_view name) {
302   auto policies_or = ParseRulesArray(json, name);
303   if (!policies_or.ok()) return policies_or.status();
304   return Rbac(Rbac::Action::kDeny, std::move(policies_or.value()));
305 }
306
307 absl::StatusOr<Rbac> ParseAllowRulesArray(const Json& json,
308                                           absl::string_view name) {
309   auto policies_or = ParseRulesArray(json, name);
310   if (!policies_or.ok()) return policies_or.status();
311   return Rbac(Rbac::Action::kAllow, std::move(policies_or.value()));
312 }
313
314 }  // namespace
315
316 absl::StatusOr<RbacPolicies> GenerateRbacPolicies(
317     absl::string_view authz_policy) {
318   grpc_error_handle error = GRPC_ERROR_NONE;
319   Json json = Json::Parse(authz_policy, &error);
320   if (error != GRPC_ERROR_NONE) {
321     absl::Status status = absl::InvalidArgumentError(
322         absl::StrCat("Failed to parse SDK authorization policy. Error: ",
323                      grpc_error_std_string(error)));
324     GRPC_ERROR_UNREF(error);
325     return status;
326   }
327   if (json.type() != Json::Type::OBJECT) {
328     return absl::InvalidArgumentError(
329         "SDK authorization policy is not an object.");
330   }
331   auto it = json.mutable_object()->find("name");
332   if (it == json.mutable_object()->end()) {
333     return absl::InvalidArgumentError("\"name\" field is not present.");
334   }
335   if (it->second.type() != Json::Type::STRING) {
336     return absl::InvalidArgumentError("\"name\" is not a string.");
337   }
338   absl::string_view name = it->second.string_value();
339   RbacPolicies rbac_policies;
340   it = json.mutable_object()->find("deny_rules");
341   if (it != json.mutable_object()->end()) {
342     if (it->second.type() != Json::Type::ARRAY) {
343       return absl::InvalidArgumentError("\"deny_rules\" is not an array.");
344     }
345     auto deny_policy_or = ParseDenyRulesArray(it->second, name);
346     if (!deny_policy_or.ok()) {
347       return absl::Status(
348           deny_policy_or.status().code(),
349           absl::StrCat("deny_", deny_policy_or.status().message()));
350     }
351     rbac_policies.deny_policy = std::move(deny_policy_or.value());
352   } else {
353     rbac_policies.deny_policy.action = Rbac::Action::kDeny;
354   }
355   it = json.mutable_object()->find("allow_rules");
356   if (it == json.mutable_object()->end()) {
357     return absl::InvalidArgumentError("\"allow_rules\" is not present.");
358   }
359   if (it->second.type() != Json::Type::ARRAY) {
360     return absl::InvalidArgumentError("\"allow_rules\" is not an array.");
361   }
362   auto allow_policy_or = ParseAllowRulesArray(it->second, name);
363   if (!allow_policy_or.ok()) {
364     return absl::Status(
365         allow_policy_or.status().code(),
366         absl::StrCat("allow_", allow_policy_or.status().message()));
367   }
368   rbac_policies.allow_policy = std::move(allow_policy_or.value());
369   return std::move(rbac_policies);
370 }
371
372 }  // namespace grpc_core