1 // Copyright 2021 gRPC authors.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include <grpc/support/port_platform.h>
17 #include "src/core/lib/security/authorization/rbac_translator.h"
19 #include "absl/strings/str_cat.h"
20 #include "absl/strings/str_format.h"
21 #include "absl/strings/strip.h"
23 #include "src/core/lib/gpr/useful.h"
24 #include "src/core/lib/matchers/matchers.h"
30 absl::string_view GetMatcherType(absl::string_view value,
31 StringMatcher::Type* type) {
33 *type = StringMatcher::Type::kPrefix;
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, "*");
42 *type = StringMatcher::Type::kExact;
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);
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),
60 bool IsUnsupportedHeader(absl::string_view header_name) {
61 static const char* const kUnsupportedHeaders[] = {"host",
65 "proxy-authorization",
70 for (size_t i = 0; i < GPR_ARRAY_SIZE(kUnsupportedHeaders); ++i) {
71 if (absl::EqualsIgnoreCase(header_name, kUnsupportedHeaders[i])) {
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."));
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()));
92 principal_names.push_back(absl::make_unique<Rbac::Principal>(
93 Rbac::Principal::RuleType::kPrincipalName,
94 std::move(matcher_or.value())));
96 return Rbac::Principal(Rbac::Principal::RuleType::kOr,
97 std::move(principal_names));
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.");
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())));
115 return Rbac::Principal(Rbac::Principal::RuleType::kAny);
117 return Rbac::Principal(Rbac::Principal::RuleType::kAnd, std::move(peer));
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.");
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."));
132 auto matcher_or = GetHeaderMatcher(header_name, child.string_value());
133 if (!matcher_or.ok()) {
135 matcher_or.status().code(),
136 absl::StrCat("\"values\" ", i, ": ", matcher_or.status().message()));
138 values.push_back(absl::make_unique<Rbac::Permission>(
139 Rbac::Permission::RuleType::kHeader, std::move(matcher_or.value())));
141 return Rbac::Permission(Rbac::Permission::RuleType::kOr, std::move(values));
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.");
149 if (it->second.type() != Json::Type::STRING) {
150 return absl::InvalidArgumentError("\"key\" is not a string.");
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));
159 it = json.object_value().find("values");
160 if (it == json.object_value().end()) {
161 return absl::InvalidArgumentError("\"values\" is not present.");
163 if (it->second.type() != Json::Type::ARRAY) {
164 return absl::InvalidArgumentError("\"values\" is not an array.");
166 return ParseHeaderValues(it->second, header_name);
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."));
177 auto headers_or = ParseHeaders(child);
178 if (!headers_or.ok()) {
180 headers_or.status().code(),
181 absl::StrCat("\"headers\" ", i, ": ", headers_or.status().message()));
184 absl::make_unique<Rbac::Permission>(std::move(headers_or.value())));
186 return Rbac::Permission(Rbac::Permission::RuleType::kAnd, std::move(headers));
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."));
197 auto matcher_or = GetStringMatcher(child.string_value());
198 if (!matcher_or.ok()) {
200 matcher_or.status().code(),
201 absl::StrCat("\"paths\" ", i, ": ", matcher_or.status().message()));
203 paths.push_back(absl::make_unique<Rbac::Permission>(
204 Rbac::Permission::RuleType::kPath, std::move(matcher_or.value())));
206 return Rbac::Permission(Rbac::Permission::RuleType::kOr, std::move(paths));
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.");
216 auto paths_or = ParsePathsArray(it->second);
217 if (!paths_or.ok()) return paths_or.status();
218 if (!paths_or.value().permissions.empty()) {
220 absl::make_unique<Rbac::Permission>(std::move(paths_or.value())));
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.");
228 auto headers_or = ParseHeadersArray(it->second);
229 if (!headers_or.ok()) return headers_or.status();
230 if (!headers_or.value().permissions.empty()) {
232 absl::make_unique<Rbac::Permission>(std::move(headers_or.value())));
235 if (request.empty()) {
236 return Rbac::Permission(Rbac::Permission::RuleType::kAny);
238 return Rbac::Permission(Rbac::Permission::RuleType::kAnd, std::move(request));
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.");
248 auto peer_or = ParsePeer(it->second);
249 if (!peer_or.ok()) return peer_or.status();
250 principals = std::move(peer_or.value());
252 principals = Rbac::Principal(Rbac::Principal::RuleType::kAny);
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.");
260 auto request_or = ParseRequest(it->second);
261 if (!request_or.ok()) return request_or.status();
262 permissions = std::move(request_or.value());
264 permissions = Rbac::Permission(Rbac::Permission::RuleType::kAny);
266 return Rbac::Policy(std::move(permissions), std::move(principals));
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."));
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."));
283 if (it->second.type() != Json::Type::STRING) {
284 return absl::InvalidArgumentError(
285 absl::StrCat("rules ", i, ": \"name\" is not a string."));
287 std::string policy_name =
288 std::string(name) + "_" + it->second.string_value();
289 auto policy_or = ParseRules(child);
290 if (!policy_or.ok()) {
292 policy_or.status().code(),
293 absl::StrCat("rules ", i, ": ", policy_or.status().message()));
295 policies[policy_name] = std::move(policy_or.value());
297 return std::move(policies);
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()));
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()));
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);
327 if (json.type() != Json::Type::OBJECT) {
328 return absl::InvalidArgumentError(
329 "SDK authorization policy is not an object.");
331 auto it = json.mutable_object()->find("name");
332 if (it == json.mutable_object()->end()) {
333 return absl::InvalidArgumentError("\"name\" field is not present.");
335 if (it->second.type() != Json::Type::STRING) {
336 return absl::InvalidArgumentError("\"name\" is not a string.");
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.");
345 auto deny_policy_or = ParseDenyRulesArray(it->second, name);
346 if (!deny_policy_or.ok()) {
348 deny_policy_or.status().code(),
349 absl::StrCat("deny_", deny_policy_or.status().message()));
351 rbac_policies.deny_policy = std::move(deny_policy_or.value());
353 rbac_policies.deny_policy.action = Rbac::Action::kDeny;
355 it = json.mutable_object()->find("allow_rules");
356 if (it == json.mutable_object()->end()) {
357 return absl::InvalidArgumentError("\"allow_rules\" is not present.");
359 if (it->second.type() != Json::Type::ARRAY) {
360 return absl::InvalidArgumentError("\"allow_rules\" is not an array.");
362 auto allow_policy_or = ParseAllowRulesArray(it->second, name);
363 if (!allow_policy_or.ok()) {
365 allow_policy_or.status().code(),
366 absl::StrCat("allow_", allow_policy_or.status().message()));
368 rbac_policies.allow_policy = std::move(allow_policy_or.value());
369 return std::move(rbac_policies);
372 } // namespace grpc_core