• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/matchers/matchers.h"
24 
25 namespace grpc_core {
26 
27 namespace {
28 
GetMatcherType(absl::string_view value,StringMatcher::Type * type)29 absl::string_view GetMatcherType(absl::string_view value,
30                                  StringMatcher::Type* type) {
31   if (value == "*") {
32     *type = StringMatcher::Type::kPrefix;
33     return "";
34   } else if (absl::StartsWith(value, "*")) {
35     *type = StringMatcher::Type::kSuffix;
36     return absl::StripPrefix(value, "*");
37   } else if (absl::EndsWith(value, "*")) {
38     *type = StringMatcher::Type::kPrefix;
39     return absl::StripSuffix(value, "*");
40   }
41   *type = StringMatcher::Type::kExact;
42   return value;
43 }
44 
GetStringMatcher(absl::string_view value)45 absl::StatusOr<StringMatcher> GetStringMatcher(absl::string_view value) {
46   StringMatcher::Type type;
47   absl::string_view matcher = GetMatcherType(value, &type);
48   return StringMatcher::Create(type, matcher);
49 }
50 
GetHeaderMatcher(absl::string_view name,absl::string_view value)51 absl::StatusOr<HeaderMatcher> GetHeaderMatcher(absl::string_view name,
52                                                absl::string_view value) {
53   StringMatcher::Type type;
54   absl::string_view matcher = GetMatcherType(value, &type);
55   return HeaderMatcher::Create(name, static_cast<HeaderMatcher::Type>(type),
56                                matcher);
57 }
58 
ParsePrincipalsArray(const Json & json)59 absl::StatusOr<Rbac::Principal> ParsePrincipalsArray(const Json& json) {
60   std::vector<std::unique_ptr<Rbac::Principal>> principal_names;
61   for (size_t i = 0; i < json.array_value().size(); ++i) {
62     const Json& child = json.array_value().at(i);
63     if (child.type() != Json::Type::STRING) {
64       return absl::InvalidArgumentError(
65           absl::StrCat("\"principals\" ", i, ": is not a string."));
66     }
67     auto matcher_or = GetStringMatcher(child.string_value());
68     if (!matcher_or.ok()) {
69       return absl::Status(matcher_or.status().code(),
70                           absl::StrCat("\"principals\" ", i, ": ",
71                                        matcher_or.status().message()));
72     }
73     principal_names.push_back(absl::make_unique<Rbac::Principal>(
74         Rbac::Principal::RuleType::kPrincipalName,
75         std::move(matcher_or.value())));
76   }
77   return Rbac::Principal(Rbac::Principal::RuleType::kOr,
78                          std::move(principal_names));
79 }
80 
ParsePeer(const Json & json)81 absl::StatusOr<Rbac::Principal> ParsePeer(const Json& json) {
82   std::vector<std::unique_ptr<Rbac::Principal>> peer;
83   auto it = json.object_value().find("principals");
84   if (it != json.object_value().end()) {
85     if (it->second.type() != Json::Type::ARRAY) {
86       return absl::InvalidArgumentError("\"principals\" is not an array.");
87     }
88     auto principal_names_or = ParsePrincipalsArray(it->second);
89     if (!principal_names_or.ok()) return principal_names_or.status();
90     if (!principal_names_or.value().principals.empty()) {
91       peer.push_back(absl::make_unique<Rbac::Principal>(
92           std::move(principal_names_or.value())));
93     }
94   }
95   if (peer.empty()) {
96     return Rbac::Principal(Rbac::Principal::RuleType::kAny);
97   }
98   return Rbac::Principal(Rbac::Principal::RuleType::kAnd, std::move(peer));
99 }
100 
ParseHeaderValues(const Json & json,absl::string_view header_name)101 absl::StatusOr<Rbac::Permission> ParseHeaderValues(
102     const Json& json, absl::string_view header_name) {
103   if (json.array_value().empty()) {
104     return absl::InvalidArgumentError("\"values\" list is empty.");
105   }
106   std::vector<std::unique_ptr<Rbac::Permission>> values;
107   for (size_t i = 0; i < json.array_value().size(); ++i) {
108     const Json& child = json.array_value().at(i);
109     if (child.type() != Json::Type::STRING) {
110       return absl::InvalidArgumentError(
111           absl::StrCat("\"values\" ", i, ": is not a string."));
112     }
113     auto matcher_or = GetHeaderMatcher(header_name, child.string_value());
114     if (!matcher_or.ok()) {
115       return absl::Status(
116           matcher_or.status().code(),
117           absl::StrCat("\"values\" ", i, ": ", matcher_or.status().message()));
118     }
119     values.push_back(absl::make_unique<Rbac::Permission>(
120         Rbac::Permission::RuleType::kHeader, std::move(matcher_or.value())));
121   }
122   return Rbac::Permission(Rbac::Permission::RuleType::kOr, std::move(values));
123 }
124 
ParseHeaders(const Json & json)125 absl::StatusOr<Rbac::Permission> ParseHeaders(const Json& json) {
126   auto it = json.object_value().find("key");
127   if (it == json.object_value().end()) {
128     return absl::InvalidArgumentError("\"key\" is not present.");
129   }
130   if (it->second.type() != Json::Type::STRING) {
131     return absl::InvalidArgumentError("\"key\" is not a string.");
132   }
133   absl::string_view header_name = it->second.string_value();
134   // TODO(ashithasantosh): Add connection headers below.
135   if (absl::StartsWith(header_name, ":") ||
136       absl::StartsWith(header_name, "grpc-") || header_name == "host" ||
137       header_name == "Host") {
138     return absl::InvalidArgumentError(
139         absl::StrFormat("Unsupported \"key\" %s.", header_name));
140   }
141   it = json.object_value().find("values");
142   if (it == json.object_value().end()) {
143     return absl::InvalidArgumentError("\"values\" is not present.");
144   }
145   if (it->second.type() != Json::Type::ARRAY) {
146     return absl::InvalidArgumentError("\"values\" is not an array.");
147   }
148   return ParseHeaderValues(it->second, header_name);
149 }
150 
ParseHeadersArray(const Json & json)151 absl::StatusOr<Rbac::Permission> ParseHeadersArray(const Json& json) {
152   std::vector<std::unique_ptr<Rbac::Permission>> headers;
153   for (size_t i = 0; i < json.array_value().size(); ++i) {
154     const Json& child = json.array_value().at(i);
155     if (child.type() != Json::Type::OBJECT) {
156       return absl::InvalidArgumentError(
157           absl::StrCat("\"headers\" ", i, ": is not an object."));
158     }
159     auto headers_or = ParseHeaders(child);
160     if (!headers_or.ok()) {
161       return absl::Status(
162           headers_or.status().code(),
163           absl::StrCat("\"headers\" ", i, ": ", headers_or.status().message()));
164     }
165     headers.push_back(
166         absl::make_unique<Rbac::Permission>(std::move(headers_or.value())));
167   }
168   return Rbac::Permission(Rbac::Permission::RuleType::kAnd, std::move(headers));
169 }
170 
ParsePathsArray(const Json & json)171 absl::StatusOr<Rbac::Permission> ParsePathsArray(const Json& json) {
172   std::vector<std::unique_ptr<Rbac::Permission>> paths;
173   for (size_t i = 0; i < json.array_value().size(); ++i) {
174     const Json& child = json.array_value().at(i);
175     if (child.type() != Json::Type::STRING) {
176       return absl::InvalidArgumentError(
177           absl::StrCat("\"paths\" ", i, ": is not a string."));
178     }
179     auto matcher_or = GetStringMatcher(child.string_value());
180     if (!matcher_or.ok()) {
181       return absl::Status(
182           matcher_or.status().code(),
183           absl::StrCat("\"paths\" ", i, ": ", matcher_or.status().message()));
184     }
185     paths.push_back(absl::make_unique<Rbac::Permission>(
186         Rbac::Permission::RuleType::kPath, std::move(matcher_or.value())));
187   }
188   return Rbac::Permission(Rbac::Permission::RuleType::kOr, std::move(paths));
189 }
190 
ParseRequest(const Json & json)191 absl::StatusOr<Rbac::Permission> ParseRequest(const Json& json) {
192   std::vector<std::unique_ptr<Rbac::Permission>> request;
193   auto it = json.object_value().find("paths");
194   if (it != json.object_value().end()) {
195     if (it->second.type() != Json::Type::ARRAY) {
196       return absl::InvalidArgumentError("\"paths\" is not an array.");
197     }
198     auto paths_or = ParsePathsArray(it->second);
199     if (!paths_or.ok()) return paths_or.status();
200     if (!paths_or.value().permissions.empty()) {
201       request.push_back(
202           absl::make_unique<Rbac::Permission>(std::move(paths_or.value())));
203     }
204   }
205   it = json.object_value().find("headers");
206   if (it != json.object_value().end()) {
207     if (it->second.type() != Json::Type::ARRAY) {
208       return absl::InvalidArgumentError("\"headers\" is not an array.");
209     }
210     auto headers_or = ParseHeadersArray(it->second);
211     if (!headers_or.ok()) return headers_or.status();
212     if (!headers_or.value().permissions.empty()) {
213       request.push_back(
214           absl::make_unique<Rbac::Permission>(std::move(headers_or.value())));
215     }
216   }
217   if (request.empty()) {
218     return Rbac::Permission(Rbac::Permission::RuleType::kAny);
219   }
220   return Rbac::Permission(Rbac::Permission::RuleType::kAnd, std::move(request));
221 }
222 
ParseRules(const Json & json)223 absl::StatusOr<Rbac::Policy> ParseRules(const Json& json) {
224   Rbac::Principal principals;
225   auto it = json.object_value().find("source");
226   if (it != json.object_value().end()) {
227     if (it->second.type() != Json::Type::OBJECT) {
228       return absl::InvalidArgumentError("\"source\" is not an object.");
229     }
230     auto peer_or = ParsePeer(it->second);
231     if (!peer_or.ok()) return peer_or.status();
232     principals = std::move(peer_or.value());
233   } else {
234     principals = Rbac::Principal(Rbac::Principal::RuleType::kAny);
235   }
236   Rbac::Permission permissions;
237   it = json.object_value().find("request");
238   if (it != json.object_value().end()) {
239     if (it->second.type() != Json::Type::OBJECT) {
240       return absl::InvalidArgumentError("\"request\" is not an object.");
241     }
242     auto request_or = ParseRequest(it->second);
243     if (!request_or.ok()) return request_or.status();
244     permissions = std::move(request_or.value());
245   } else {
246     permissions = Rbac::Permission(Rbac::Permission::RuleType::kAny);
247   }
248   return Rbac::Policy(std::move(permissions), std::move(principals));
249 }
250 
ParseRulesArray(const Json & json,absl::string_view name)251 absl::StatusOr<std::map<std::string, Rbac::Policy>> ParseRulesArray(
252     const Json& json, absl::string_view name) {
253   std::map<std::string, Rbac::Policy> policies;
254   for (size_t i = 0; i < json.array_value().size(); ++i) {
255     const Json& child = json.array_value().at(i);
256     if (child.type() != Json::Type::OBJECT) {
257       return absl::InvalidArgumentError(
258           absl::StrCat("rules ", i, ": is not an object."));
259     }
260     auto it = child.object_value().find("name");
261     if (it == child.object_value().end()) {
262       return absl::InvalidArgumentError(
263           absl::StrCat("rules ", i, ": \"name\" is not present."));
264     }
265     if (it->second.type() != Json::Type::STRING) {
266       return absl::InvalidArgumentError(
267           absl::StrCat("rules ", i, ": \"name\" is not a string."));
268     }
269     std::string policy_name =
270         std::string(name) + "_" + it->second.string_value();
271     auto policy_or = ParseRules(child);
272     if (!policy_or.ok()) {
273       return absl::Status(
274           policy_or.status().code(),
275           absl::StrCat("rules ", i, ": ", policy_or.status().message()));
276     }
277     policies[policy_name] = std::move(policy_or.value());
278   }
279   return std::move(policies);
280 }
281 
ParseDenyRulesArray(const Json & json,absl::string_view name)282 absl::StatusOr<Rbac> ParseDenyRulesArray(const Json& json,
283                                          absl::string_view name) {
284   auto policies_or = ParseRulesArray(json, name);
285   if (!policies_or.ok()) return policies_or.status();
286   return Rbac(Rbac::Action::kDeny, std::move(policies_or.value()));
287 }
288 
ParseAllowRulesArray(const Json & json,absl::string_view name)289 absl::StatusOr<Rbac> ParseAllowRulesArray(const Json& json,
290                                           absl::string_view name) {
291   auto policies_or = ParseRulesArray(json, name);
292   if (!policies_or.ok()) return policies_or.status();
293   return Rbac(Rbac::Action::kAllow, std::move(policies_or.value()));
294 }
295 
296 }  // namespace
297 
GenerateRbacPolicies(absl::string_view authz_policy)298 absl::StatusOr<RbacPolicies> GenerateRbacPolicies(
299     absl::string_view authz_policy) {
300   grpc_error_handle error = GRPC_ERROR_NONE;
301   Json json = Json::Parse(authz_policy, &error);
302   if (error != GRPC_ERROR_NONE) {
303     absl::Status status = absl::InvalidArgumentError(
304         absl::StrCat("Failed to parse SDK authorization policy. Error: ",
305                      grpc_error_std_string(error)));
306     GRPC_ERROR_UNREF(error);
307     return status;
308   }
309   if (json.type() != Json::Type::OBJECT) {
310     return absl::InvalidArgumentError(
311         "SDK authorization policy is not an object.");
312   }
313   auto it = json.mutable_object()->find("name");
314   if (it == json.mutable_object()->end()) {
315     return absl::InvalidArgumentError("\"name\" field is not present.");
316   }
317   if (it->second.type() != Json::Type::STRING) {
318     return absl::InvalidArgumentError("\"name\" is not a string.");
319   }
320   absl::string_view name = it->second.string_value();
321   RbacPolicies rbac_policies;
322   it = json.mutable_object()->find("deny_rules");
323   if (it != json.mutable_object()->end()) {
324     if (it->second.type() != Json::Type::ARRAY) {
325       return absl::InvalidArgumentError("\"deny_rules\" is not an array.");
326     }
327     auto deny_policy_or = ParseDenyRulesArray(it->second, name);
328     if (!deny_policy_or.ok()) {
329       return absl::Status(
330           deny_policy_or.status().code(),
331           absl::StrCat("deny_", deny_policy_or.status().message()));
332     }
333     rbac_policies.deny_policy = std::move(deny_policy_or.value());
334   } else {
335     rbac_policies.deny_policy.action = Rbac::Action::kDeny;
336   }
337   it = json.mutable_object()->find("allow_rules");
338   if (it == json.mutable_object()->end()) {
339     return absl::InvalidArgumentError("\"allow_rules\" is not present.");
340   }
341   if (it->second.type() != Json::Type::ARRAY) {
342     return absl::InvalidArgumentError("\"allow_rules\" is not an array.");
343   }
344   auto allow_policy_or = ParseAllowRulesArray(it->second, name);
345   if (!allow_policy_or.ok()) {
346     return absl::Status(
347         allow_policy_or.status().code(),
348         absl::StrCat("allow_", allow_policy_or.status().message()));
349   }
350   rbac_policies.allow_policy = std::move(allow_policy_or.value());
351   return std::move(rbac_policies);
352 }
353 
354 }  // namespace grpc_core
355