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