rbac_translator.cc
Go to the documentation of this file.
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 
16 
18 
19 #include <stddef.h>
20 
21 #include <algorithm>
22 #include <map>
23 #include <memory>
24 #include <string>
25 #include <type_traits>
26 #include <utility>
27 #include <vector>
28 
29 #include "absl/memory/memory.h"
30 #include "absl/status/status.h"
31 #include "absl/strings/match.h"
32 #include "absl/strings/str_cat.h"
33 #include "absl/strings/str_format.h"
34 #include "absl/strings/strip.h"
35 
38 #include "src/core/lib/json/json.h"
40 
41 namespace grpc_core {
42 
43 namespace {
44 
47  if (value == "*") {
49  // Presence match checks for non empty strings.
50  return ".+";
51  } else if (absl::StartsWith(value, "*")) {
53  return absl::StripPrefix(value, "*");
54  } else if (absl::EndsWith(value, "*")) {
56  return absl::StripSuffix(value, "*");
57  }
59  return value;
60 }
61 
64  absl::string_view matcher = GetMatcherType(value, &type);
65  return StringMatcher::Create(type, matcher);
66 }
67 
71  absl::string_view matcher = GetMatcherType(value, &type);
72  return HeaderMatcher::Create(name, static_cast<HeaderMatcher::Type>(type),
73  matcher);
74 }
75 
76 bool IsUnsupportedHeader(absl::string_view header_name) {
77  static const char* const kUnsupportedHeaders[] = {"host",
78  "connection",
79  "keep-alive",
80  "proxy-authenticate",
81  "proxy-authorization",
82  "te",
83  "trailer",
84  "transfer-encoding",
85  "upgrade"};
86  for (size_t i = 0; i < GPR_ARRAY_SIZE(kUnsupportedHeaders); ++i) {
87  if (absl::EqualsIgnoreCase(header_name, kUnsupportedHeaders[i])) {
88  return true;
89  }
90  }
91  return false;
92 }
93 
94 absl::StatusOr<Rbac::Principal> ParsePrincipalsArray(const Json& json) {
95  std::vector<std::unique_ptr<Rbac::Principal>> principal_names;
96  for (size_t i = 0; i < json.array_value().size(); ++i) {
97  const Json& child = json.array_value().at(i);
98  if (child.type() != Json::Type::STRING) {
100  absl::StrCat("\"principals\" ", i, ": is not a string."));
101  }
102  auto matcher_or = GetStringMatcher(child.string_value());
103  if (!matcher_or.ok()) {
104  return absl::Status(matcher_or.status().code(),
105  absl::StrCat("\"principals\" ", i, ": ",
106  matcher_or.status().message()));
107  }
108  principal_names.push_back(absl::make_unique<Rbac::Principal>(
110  std::move(matcher_or.value()))));
111  }
112  return Rbac::Principal::MakeOrPrincipal(std::move(principal_names));
113 }
114 
115 absl::StatusOr<Rbac::Principal> ParsePeer(const Json& json) {
116  std::vector<std::unique_ptr<Rbac::Principal>> peer;
117  auto it = json.object_value().find("principals");
118  if (it != json.object_value().end()) {
119  if (it->second.type() != Json::Type::ARRAY) {
120  return absl::InvalidArgumentError("\"principals\" is not an array.");
121  }
122  auto principal_names_or = ParsePrincipalsArray(it->second);
123  if (!principal_names_or.ok()) return principal_names_or.status();
124  if (!principal_names_or.value().principals.empty()) {
125  peer.push_back(absl::make_unique<Rbac::Principal>(
126  std::move(principal_names_or.value())));
127  }
128  }
129  if (peer.empty()) {
131  }
133 }
134 
135 absl::StatusOr<Rbac::Permission> ParseHeaderValues(
136  const Json& json, absl::string_view header_name) {
137  if (json.array_value().empty()) {
138  return absl::InvalidArgumentError("\"values\" list is empty.");
139  }
140  std::vector<std::unique_ptr<Rbac::Permission>> values;
141  for (size_t i = 0; i < json.array_value().size(); ++i) {
142  const Json& child = json.array_value().at(i);
143  if (child.type() != Json::Type::STRING) {
145  absl::StrCat("\"values\" ", i, ": is not a string."));
146  }
147  auto matcher_or = GetHeaderMatcher(header_name, child.string_value());
148  if (!matcher_or.ok()) {
149  return absl::Status(
150  matcher_or.status().code(),
151  absl::StrCat("\"values\" ", i, ": ", matcher_or.status().message()));
152  }
153  values.push_back(absl::make_unique<Rbac::Permission>(
154  Rbac::Permission::MakeHeaderPermission(std::move(matcher_or.value()))));
155  }
157 }
158 
159 absl::StatusOr<Rbac::Permission> ParseHeaders(const Json& json) {
160  auto it = json.object_value().find("key");
161  if (it == json.object_value().end()) {
162  return absl::InvalidArgumentError("\"key\" is not present.");
163  }
164  if (it->second.type() != Json::Type::STRING) {
165  return absl::InvalidArgumentError("\"key\" is not a string.");
166  }
167  absl::string_view header_name = it->second.string_value();
168  if (absl::StartsWith(header_name, ":") ||
169  absl::StartsWith(header_name, "grpc-") ||
170  IsUnsupportedHeader(header_name)) {
172  absl::StrFormat("Unsupported \"key\" %s.", header_name));
173  }
174  it = json.object_value().find("values");
175  if (it == json.object_value().end()) {
176  return absl::InvalidArgumentError("\"values\" is not present.");
177  }
178  if (it->second.type() != Json::Type::ARRAY) {
179  return absl::InvalidArgumentError("\"values\" is not an array.");
180  }
181  return ParseHeaderValues(it->second, header_name);
182 }
183 
184 absl::StatusOr<Rbac::Permission> ParseHeadersArray(const Json& json) {
185  std::vector<std::unique_ptr<Rbac::Permission>> headers;
186  for (size_t i = 0; i < json.array_value().size(); ++i) {
187  const Json& child = json.array_value().at(i);
188  if (child.type() != Json::Type::OBJECT) {
190  absl::StrCat("\"headers\" ", i, ": is not an object."));
191  }
192  auto headers_or = ParseHeaders(child);
193  if (!headers_or.ok()) {
194  return absl::Status(
195  headers_or.status().code(),
196  absl::StrCat("\"headers\" ", i, ": ", headers_or.status().message()));
197  }
198  headers.push_back(
199  absl::make_unique<Rbac::Permission>(std::move(headers_or.value())));
200  }
202 }
203 
204 absl::StatusOr<Rbac::Permission> ParsePathsArray(const Json& json) {
205  std::vector<std::unique_ptr<Rbac::Permission>> paths;
206  for (size_t i = 0; i < json.array_value().size(); ++i) {
207  const Json& child = json.array_value().at(i);
208  if (child.type() != Json::Type::STRING) {
210  absl::StrCat("\"paths\" ", i, ": is not a string."));
211  }
212  auto matcher_or = GetStringMatcher(child.string_value());
213  if (!matcher_or.ok()) {
214  return absl::Status(
215  matcher_or.status().code(),
216  absl::StrCat("\"paths\" ", i, ": ", matcher_or.status().message()));
217  }
218  paths.push_back(absl::make_unique<Rbac::Permission>(
219  Rbac::Permission::MakePathPermission(std::move(matcher_or.value()))));
220  }
222 }
223 
224 absl::StatusOr<Rbac::Permission> ParseRequest(const Json& json) {
225  std::vector<std::unique_ptr<Rbac::Permission>> request;
226  auto it = json.object_value().find("paths");
227  if (it != json.object_value().end()) {
228  if (it->second.type() != Json::Type::ARRAY) {
229  return absl::InvalidArgumentError("\"paths\" is not an array.");
230  }
231  auto paths_or = ParsePathsArray(it->second);
232  if (!paths_or.ok()) return paths_or.status();
233  if (!paths_or.value().permissions.empty()) {
234  request.push_back(
235  absl::make_unique<Rbac::Permission>(std::move(paths_or.value())));
236  }
237  }
238  it = json.object_value().find("headers");
239  if (it != json.object_value().end()) {
240  if (it->second.type() != Json::Type::ARRAY) {
241  return absl::InvalidArgumentError("\"headers\" is not an array.");
242  }
243  auto headers_or = ParseHeadersArray(it->second);
244  if (!headers_or.ok()) return headers_or.status();
245  if (!headers_or.value().permissions.empty()) {
246  request.push_back(
247  absl::make_unique<Rbac::Permission>(std::move(headers_or.value())));
248  }
249  }
250  if (request.empty()) {
252  }
254 }
255 
256 absl::StatusOr<Rbac::Policy> ParseRules(const Json& json) {
257  Rbac::Principal principals;
258  auto it = json.object_value().find("source");
259  if (it != json.object_value().end()) {
260  if (it->second.type() != Json::Type::OBJECT) {
261  return absl::InvalidArgumentError("\"source\" is not an object.");
262  }
263  auto peer_or = ParsePeer(it->second);
264  if (!peer_or.ok()) return peer_or.status();
265  principals = std::move(peer_or.value());
266  } else {
267  principals = Rbac::Principal::MakeAnyPrincipal();
268  }
269  Rbac::Permission permissions;
270  it = json.object_value().find("request");
271  if (it != json.object_value().end()) {
272  if (it->second.type() != Json::Type::OBJECT) {
273  return absl::InvalidArgumentError("\"request\" is not an object.");
274  }
275  auto request_or = ParseRequest(it->second);
276  if (!request_or.ok()) return request_or.status();
277  permissions = std::move(request_or.value());
278  } else {
279  permissions = Rbac::Permission::MakeAnyPermission();
280  }
281  return Rbac::Policy(std::move(permissions), std::move(principals));
282 }
283 
285  const Json& json, absl::string_view name) {
286  std::map<std::string, Rbac::Policy> policies;
287  for (size_t i = 0; i < json.array_value().size(); ++i) {
288  const Json& child = json.array_value().at(i);
289  if (child.type() != Json::Type::OBJECT) {
291  absl::StrCat("rules ", i, ": is not an object."));
292  }
293  auto it = child.object_value().find("name");
294  if (it == child.object_value().end()) {
296  absl::StrCat("rules ", i, ": \"name\" is not present."));
297  }
298  if (it->second.type() != Json::Type::STRING) {
300  absl::StrCat("rules ", i, ": \"name\" is not a string."));
301  }
302  std::string policy_name =
303  std::string(name) + "_" + it->second.string_value();
304  auto policy_or = ParseRules(child);
305  if (!policy_or.ok()) {
306  return absl::Status(
307  policy_or.status().code(),
308  absl::StrCat("rules ", i, ": ", policy_or.status().message()));
309  }
310  policies[policy_name] = std::move(policy_or.value());
311  }
312  return std::move(policies);
313 }
314 
315 absl::StatusOr<Rbac> ParseDenyRulesArray(const Json& json,
317  auto policies_or = ParseRulesArray(json, name);
318  if (!policies_or.ok()) return policies_or.status();
319  return Rbac(Rbac::Action::kDeny, std::move(policies_or.value()));
320 }
321 
322 absl::StatusOr<Rbac> ParseAllowRulesArray(const Json& json,
324  auto policies_or = ParseRulesArray(json, name);
325  if (!policies_or.ok()) return policies_or.status();
326  return Rbac(Rbac::Action::kAllow, std::move(policies_or.value()));
327 }
328 
329 } // namespace
330 
332  absl::string_view authz_policy) {
334  Json json = Json::Parse(authz_policy, &error);
335  if (!GRPC_ERROR_IS_NONE(error)) {
337  absl::StrCat("Failed to parse gRPC authorization policy. Error: ",
340  return status;
341  }
342  if (json.type() != Json::Type::OBJECT) {
344  "SDK authorization policy is not an object.");
345  }
346  auto it = json.mutable_object()->find("name");
347  if (it == json.mutable_object()->end()) {
348  return absl::InvalidArgumentError("\"name\" field is not present.");
349  }
350  if (it->second.type() != Json::Type::STRING) {
351  return absl::InvalidArgumentError("\"name\" is not a string.");
352  }
353  absl::string_view name = it->second.string_value();
354  RbacPolicies rbac_policies;
355  it = json.mutable_object()->find("deny_rules");
356  if (it != json.mutable_object()->end()) {
357  if (it->second.type() != Json::Type::ARRAY) {
358  return absl::InvalidArgumentError("\"deny_rules\" is not an array.");
359  }
360  auto deny_policy_or = ParseDenyRulesArray(it->second, name);
361  if (!deny_policy_or.ok()) {
362  return absl::Status(
363  deny_policy_or.status().code(),
364  absl::StrCat("deny_", deny_policy_or.status().message()));
365  }
366  rbac_policies.deny_policy = std::move(deny_policy_or.value());
367  } else {
368  rbac_policies.deny_policy.action = Rbac::Action::kDeny;
369  }
370  it = json.mutable_object()->find("allow_rules");
371  if (it == json.mutable_object()->end()) {
372  return absl::InvalidArgumentError("\"allow_rules\" is not present.");
373  }
374  if (it->second.type() != Json::Type::ARRAY) {
375  return absl::InvalidArgumentError("\"allow_rules\" is not an array.");
376  }
377  auto allow_policy_or = ParseAllowRulesArray(it->second, name);
378  if (!allow_policy_or.ok()) {
379  return absl::Status(
380  allow_policy_or.status().code(),
381  absl::StrCat("allow_", allow_policy_or.status().message()));
382  }
383  rbac_policies.allow_policy = std::move(allow_policy_or.value());
384  return std::move(rbac_policies);
385 }
386 
387 } // namespace grpc_core
absl::InvalidArgumentError
Status InvalidArgumentError(absl::string_view message)
Definition: third_party/abseil-cpp/absl/status/status.cc:351
regen-readme.it
it
Definition: regen-readme.py:15
GRPC_ERROR_NONE
#define GRPC_ERROR_NONE
Definition: error.h:234
grpc_core::Rbac::Permission::MakePathPermission
static Permission MakePathPermission(StringMatcher string_matcher)
Definition: rbac_policy.cc:121
grpc_core::Rbac::Permission::MakeHeaderPermission
static Permission MakeHeaderPermission(HeaderMatcher header_matcher)
Definition: rbac_policy.cc:113
grpc_core::Json::type
Type type() const
Definition: src/core/lib/json/json.h:174
grpc_core::Rbac::Permission::MakeAnyPermission
static Permission MakeAnyPermission()
Definition: rbac_policy.cc:107
absl::StrCat
std::string StrCat(const AlphaNum &a, const AlphaNum &b)
Definition: abseil-cpp/absl/strings/str_cat.cc:98
grpc_core::Rbac::Permission::MakeAndPermission
static Permission MakeAndPermission(std::vector< std::unique_ptr< Permission >> permissions)
Definition: rbac_policy.cc:83
grpc_core::Rbac::Principal::MakeAnyPrincipal
static Principal MakeAnyPrincipal()
Definition: rbac_policy.cc:279
absl::StrFormat
ABSL_MUST_USE_RESULT std::string StrFormat(const FormatSpec< Args... > &format, const Args &... args)
Definition: abseil-cpp/absl/strings/str_format.h:338
grpc_core::Json::Type::OBJECT
@ OBJECT
grpc_core
Definition: call_metric_recorder.h:31
benchmark.request
request
Definition: benchmark.py:77
absl::StartsWith
bool StartsWith(absl::string_view text, absl::string_view prefix) noexcept
Definition: third_party/abseil-cpp/absl/strings/match.h:58
grpc_core::Rbac::Action::kDeny
@ kDeny
absl::string_view
Definition: abseil-cpp/absl/strings/string_view.h:167
useful.h
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
error
grpc_error_handle error
Definition: retry_filter.cc:499
status
absl::Status status
Definition: rls.cc:251
grpc_core::StringMatcher::Create
static absl::StatusOr< StringMatcher > Create(Type type, absl::string_view matcher, bool case_sensitive=true)
Definition: matchers/matchers.cc:34
setup.name
name
Definition: setup.py:542
absl::StripPrefix
ABSL_MUST_USE_RESULT absl::string_view StripPrefix(absl::string_view str, absl::string_view prefix)
Definition: abseil-cpp/absl/strings/strip.h:73
grpc_core::Json::mutable_object
Object * mutable_object()
Definition: src/core/lib/json/json.h:178
grpc_core::RbacPolicies
Definition: rbac_translator.h:27
absl::StripSuffix
ABSL_MUST_USE_RESULT absl::string_view StripSuffix(absl::string_view str, absl::string_view suffix)
Definition: abseil-cpp/absl/strings/strip.h:84
grpc_core::StringMatcher::Type::kSuffix
@ kSuffix
grpc_core::HeaderMatcher::Type
Type
Definition: matchers/matchers.h:81
absl::move
constexpr absl::remove_reference_t< T > && move(T &&t) noexcept
Definition: abseil-cpp/absl/utility/utility.h:221
grpc_core::Rbac::Principal::MakeAndPrincipal
static Principal MakeAndPrincipal(std::vector< std::unique_ptr< Principal >> principals)
Definition: rbac_policy.cc:255
Json
JSON (JavaScript Object Notation).
Definition: third_party/bloaty/third_party/protobuf/conformance/third_party/jsoncpp/json.h:227
grpc_core::Rbac::action
Action action
Definition: rbac_policy.h:172
matchers.h
absl::EqualsIgnoreCase
ABSL_NAMESPACE_BEGIN bool EqualsIgnoreCase(absl::string_view piece1, absl::string_view piece2) noexcept
Definition: abseil-cpp/absl/strings/match.cc:22
grpc_core::StringMatcher::Type
Type
Definition: matchers/matchers.h:34
grpc_core::GenerateRbacPolicies
absl::StatusOr< RbacPolicies > GenerateRbacPolicies(absl::string_view authz_policy)
Definition: rbac_translator.cc:331
googletest-filter-unittest.child
child
Definition: bloaty/third_party/googletest/googletest/test/googletest-filter-unittest.py:62
error.h
grpc_core::Json::Type::ARRAY
@ ARRAY
json.h
grpc_core::Json::Parse
static Json Parse(absl::string_view json_str, grpc_error_handle *error)
Definition: json_reader.cc:899
value
const char * value
Definition: hpack_parser_table.cc:165
absl::Status
ABSL_NAMESPACE_BEGIN class ABSL_MUST_USE_RESULT Status
Definition: abseil-cpp/absl/status/internal/status_internal.h:36
GPR_ARRAY_SIZE
#define GPR_ARRAY_SIZE(array)
Definition: useful.h:129
grpc_core::Rbac::Principal::MakeAuthenticatedPrincipal
static Principal MakeAuthenticatedPrincipal(absl::optional< StringMatcher > string_matcher)
Definition: rbac_policy.cc:285
grpc_core::Rbac::Permission::MakeOrPermission
static Permission MakeOrPermission(std::vector< std::unique_ptr< Permission >> permissions)
Definition: rbac_policy.cc:91
grpc_error_std_string
std::string grpc_error_std_string(grpc_error_handle error)
Definition: error.cc:944
absl::Status
Definition: third_party/abseil-cpp/absl/status/status.h:424
rbac_translator.h
grpc_core::HeaderMatcher::Create
static absl::StatusOr< HeaderMatcher > Create(absl::string_view name, Type type, absl::string_view matcher, int64_t range_start=0, int64_t range_end=0, bool present_match=false, bool invert_match=false)
Definition: matchers/matchers.cc:157
grpc_core::Rbac::Action::kAllow
@ kAllow
values
std::array< int64_t, Size > values
Definition: abseil-cpp/absl/container/btree_benchmark.cc:608
grpc_core::StringMatcher::Type::kExact
@ kExact
GRPC_ERROR_UNREF
#define GRPC_ERROR_UNREF(err)
Definition: error.h:262
grpc_core::StringMatcher::Type::kSafeRegex
@ kSafeRegex
absl::StatusOr
Definition: abseil-cpp/absl/status/statusor.h:187
asyncio_get_stats.type
type
Definition: asyncio_get_stats.py:37
grpc_core::Rbac::Principal::MakeOrPrincipal
static Principal MakeOrPrincipal(std::vector< std::unique_ptr< Principal >> principals)
Definition: rbac_policy.cc:263
grpc_error
Definition: error_internal.h:42
size
voidpf void uLong size
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:136
grpc_core::StringMatcher::Type::kPrefix
@ kPrefix
grpc_core::RbacPolicies::deny_policy
Rbac deny_policy
Definition: rbac_translator.h:28
grpc_core::RbacPolicies::allow_policy
Rbac allow_policy
Definition: rbac_translator.h:29
absl::EndsWith
bool EndsWith(absl::string_view text, absl::string_view suffix) noexcept
Definition: third_party/abseil-cpp/absl/strings/match.h:68
grpc_core::Json::Type::STRING
@ STRING
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
GRPC_ERROR_IS_NONE
#define GRPC_ERROR_IS_NONE(err)
Definition: error.h:241
port_platform.h


grpc
Author(s):
autogenerated on Fri May 16 2025 02:59:59