blob: 91f0ed7e4f813b526d6821b9d0d8a2618430efbe [file] [log] [blame]
//
// Copyright 2021 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <grpc/support/port_platform.h>
#include "src/core/ext/filters/rbac/rbac_service_config_parser.h"
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/types/optional.h"
#include <grpc/grpc_audit_logging.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/json/json_args.h"
#include "src/core/lib/json/json_object_loader.h"
#include "src/core/lib/matchers/matchers.h"
#include "src/core/lib/security/authorization/audit_logging.h"
namespace grpc_core {
namespace {
using experimental::AuditLoggerFactory;
using experimental::AuditLoggerRegistry;
// RbacConfig: one or more RbacPolicy structs
struct RbacConfig {
// RbacPolicy: optional Rules
struct RbacPolicy {
// Rules: an action, plus a map of policy names to Policy structs
struct Rules {
// Policy: a list of Permissions and a list of Principals
struct Policy {
// CidrRange: represents an IP range
struct CidrRange {
Rbac::CidrRange cidr_range;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json& json, const JsonArgs& args,
ValidationErrors* errors);
};
// SafeRegexMatch: a regex matcher
struct SafeRegexMatch {
std::string regex;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};
// HeaderMatch: a matcher for HTTP headers
struct HeaderMatch {
// RangeMatch: matches a range of numerical values
struct RangeMatch {
int64_t start;
int64_t end;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};
HeaderMatcher matcher;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json& json, const JsonArgs& args,
ValidationErrors* errors);
};
// StringMatch: a matcher for strings
struct StringMatch {
StringMatcher matcher;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json& json, const JsonArgs& args,
ValidationErrors* errors);
};
// PathMatch: a matcher for paths
struct PathMatch {
StringMatch path;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};
// Metadata: a matcher for Envoy metadata (not really applicable
// to gRPC; we use only the invert field for proper match semantics)
struct Metadata {
bool invert = false;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};
// Permission: a matcher for request attributes
struct Permission {
// PermissionList: a list used for "and" and "or" matchers
struct PermissionList {
std::vector<Permission> rules;
PermissionList() = default;
PermissionList(const PermissionList&) = delete;
PermissionList& operator=(const PermissionList&) = delete;
PermissionList(PermissionList&&) = default;
PermissionList& operator=(PermissionList&&) = default;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};
std::unique_ptr<Rbac::Permission> permission;
Permission() = default;
Permission(const Permission&) = delete;
Permission& operator=(const Permission&) = delete;
Permission(Permission&&) = default;
Permission& operator=(Permission&&) = default;
static std::vector<std::unique_ptr<Rbac::Permission>>
MakeRbacPermissionList(std::vector<Permission> permission_list);
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json& json, const JsonArgs& args,
ValidationErrors* errors);
};
// Principal: a matcher for client identity
struct Principal {
// PrincipalList: a list used for "and" and "or" matchers
struct PrincipalList {
std::vector<Principal> ids;
PrincipalList() = default;
PrincipalList(const PrincipalList&) = delete;
PrincipalList& operator=(const PrincipalList&) = delete;
PrincipalList(PrincipalList&&) = default;
PrincipalList& operator=(PrincipalList&&) = default;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};
struct Authenticated {
absl::optional<StringMatch> principal_name;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};
std::unique_ptr<Rbac::Principal> principal;
Principal() = default;
Principal(const Principal&) = delete;
Principal& operator=(const Principal&) = delete;
Principal(Principal&&) = default;
Principal& operator=(Principal&&) = default;
static std::vector<std::unique_ptr<Rbac::Principal>>
MakeRbacPrincipalList(std::vector<Principal> principal_list);
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json& json, const JsonArgs& args,
ValidationErrors* errors);
};
std::vector<Permission> permissions;
std::vector<Principal> principals;
Policy() = default;
Policy(const Policy&) = delete;
Policy& operator=(const Policy&) = delete;
Policy(Policy&&) = default;
Policy& operator=(Policy&&) = default;
Rbac::Policy TakeAsRbacPolicy();
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};
// AuditLogger: the name of logger and its config in json
struct AuditLogger {
std::string name;
Json::Object config;
AuditLogger() = default;
AuditLogger(const AuditLogger&) = delete;
AuditLogger& operator=(const AuditLogger&) = delete;
AuditLogger(AuditLogger&&) = default;
AuditLogger& operator=(AuditLogger&&) = default;
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json&, const JsonArgs&,
ValidationErrors* errors);
};
int action;
std::map<std::string, Policy> policies;
// Defaults to kNone since its json field is optional.
Rbac::AuditCondition audit_condition = Rbac::AuditCondition::kNone;
std::vector<std::unique_ptr<AuditLoggerFactory::Config>> logger_configs;
Rules() {}
Rules(const Rules&) = delete;
Rules& operator=(const Rules&) = delete;
Rules(Rules&&) = default;
Rules& operator=(Rules&&) = default;
Rbac TakeAsRbac(std::string name);
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json&, const JsonArgs&, ValidationErrors* errors);
};
std::string name;
absl::optional<Rules> rules;
Rbac TakeAsRbac();
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};
std::vector<RbacPolicy> rbac_policies;
std::vector<Rbac> TakeAsRbacList();
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};
//
// RbacConfig::RbacPolicy::Rules::Policy::CidrRange
//
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::Policy::CidrRange::JsonLoader(const JsonArgs&) {
// All fields handled in JsonPostLoad().
static const auto* loader = JsonObjectLoader<CidrRange>().Finish();
return loader;
}
void RbacConfig::RbacPolicy::Rules::Policy::CidrRange::JsonPostLoad(
const Json& json, const JsonArgs& args, ValidationErrors* errors) {
auto address_prefix = LoadJsonObjectField<std::string>(
json.object(), args, "addressPrefix", errors);
auto prefix_len =
LoadJsonObjectField<uint32_t>(json.object(), args, "prefixLen", errors,
/*required=*/false);
cidr_range =
Rbac::CidrRange(address_prefix.value_or(""), prefix_len.value_or(0));
}
//
// RbacConfig::RbacPolicy::Rules::Policy::SafeRegexMatch
//
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::Policy::SafeRegexMatch::JsonLoader(
const JsonArgs&) {
static const auto* loader = JsonObjectLoader<SafeRegexMatch>()
.Field("regex", &SafeRegexMatch::regex)
.Finish();
return loader;
}
//
// RbacConfig::RbacPolicy::Rules::Policy::HeaderMatch::RangeMatch
//
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::Policy::HeaderMatch::RangeMatch::JsonLoader(
const JsonArgs&) {
static const auto* loader = JsonObjectLoader<RangeMatch>()
.Field("start", &RangeMatch::start)
.Field("end", &RangeMatch::end)
.Finish();
return loader;
}
//
// RbacConfig::RbacPolicy::Rules::Policy::HeaderMatch
//
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::Policy::HeaderMatch::JsonLoader(
const JsonArgs&) {
// All fields handled in JsonPostLoad().
static const auto* loader = JsonObjectLoader<HeaderMatch>().Finish();
return loader;
}
void RbacConfig::RbacPolicy::Rules::Policy::HeaderMatch::JsonPostLoad(
const Json& json, const JsonArgs& args, ValidationErrors* errors) {
const size_t original_error_size = errors->size();
std::string name =
LoadJsonObjectField<std::string>(json.object(), args, "name", errors)
.value_or("");
bool invert_match =
LoadJsonObjectField<bool>(json.object(), args, "invertMatch", errors,
/*required=*/false)
.value_or(false);
auto set_header_matcher = [&](absl::StatusOr<HeaderMatcher> header_matcher) {
if (header_matcher.ok()) {
matcher = *header_matcher;
} else {
errors->AddError(header_matcher.status().message());
}
};
auto check_match = [&](absl::string_view field_name,
HeaderMatcher::Type type) {
auto match = LoadJsonObjectField<std::string>(json.object(), args,
field_name, errors,
/*required=*/false);
if (match.has_value()) {
set_header_matcher(
HeaderMatcher::Create(name, type, *match, 0, 0, false, invert_match));
return true;
}
return false;
};
if (check_match("exactMatch", HeaderMatcher::Type::kExact) ||
check_match("prefixMatch", HeaderMatcher::Type::kPrefix) ||
check_match("suffixMatch", HeaderMatcher::Type::kSuffix) ||
check_match("containsMatch", HeaderMatcher::Type::kContains)) {
return;
}
auto present_match =
LoadJsonObjectField<bool>(json.object(), args, "presentMatch", errors,
/*required=*/false);
if (present_match.has_value()) {
set_header_matcher(
HeaderMatcher::Create(name, HeaderMatcher::Type::kPresent, "", 0, 0,
*present_match, invert_match));
return;
}
auto regex_match = LoadJsonObjectField<SafeRegexMatch>(
json.object(), args, "safeRegexMatch", errors,
/*required=*/false);
if (regex_match.has_value()) {
set_header_matcher(
HeaderMatcher::Create(name, HeaderMatcher::Type::kSafeRegex,
regex_match->regex, 0, 0, false, invert_match));
return;
}
auto range_match =
LoadJsonObjectField<RangeMatch>(json.object(), args, "rangeMatch", errors,
/*required=*/false);
if (range_match.has_value()) {
set_header_matcher(HeaderMatcher::Create(name, HeaderMatcher::Type::kRange,
"", range_match->start,
range_match->end, invert_match));
return;
}
if (errors->size() == original_error_size) {
errors->AddError("no valid matcher found");
}
}
//
// RbacConfig::RbacPolicy::Rules::Policy::StringMatch
//
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::Policy::StringMatch::JsonLoader(
const JsonArgs&) {
// All fields handled in JsonPostLoad().
static const auto* loader = JsonObjectLoader<StringMatch>().Finish();
return loader;
}
void RbacConfig::RbacPolicy::Rules::Policy::StringMatch::JsonPostLoad(
const Json& json, const JsonArgs& args, ValidationErrors* errors) {
const size_t original_error_size = errors->size();
bool ignore_case =
LoadJsonObjectField<bool>(json.object(), args, "ignoreCase", errors,
/*required=*/false)
.value_or(false);
auto set_string_matcher = [&](absl::StatusOr<StringMatcher> string_matcher) {
if (string_matcher.ok()) {
matcher = *string_matcher;
} else {
errors->AddError(string_matcher.status().message());
}
};
auto check_match = [&](absl::string_view field_name,
StringMatcher::Type type) {
auto match = LoadJsonObjectField<std::string>(json.object(), args,
field_name, errors,
/*required=*/false);
if (match.has_value()) {
set_string_matcher(StringMatcher::Create(type, *match, ignore_case));
return true;
}
return false;
};
if (check_match("exact", StringMatcher::Type::kExact) ||
check_match("prefix", StringMatcher::Type::kPrefix) ||
check_match("suffix", StringMatcher::Type::kSuffix) ||
check_match("contains", StringMatcher::Type::kContains)) {
return;
}
auto regex_match = LoadJsonObjectField<SafeRegexMatch>(json.object(), args,
"safeRegex", errors,
/*required=*/false);
if (regex_match.has_value()) {
set_string_matcher(StringMatcher::Create(StringMatcher::Type::kSafeRegex,
regex_match->regex, ignore_case));
return;
}
if (errors->size() == original_error_size) {
errors->AddError("no valid matcher found");
}
}
//
// RbacConfig::RbacPolicy::Rules::Policy::PathMatch
//
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::Policy::PathMatch::JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<PathMatch>().Field("path", &PathMatch::path).Finish();
return loader;
}
//
// RbacConfig::RbacPolicy::Rules::Policy::Metadata
//
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::Policy::Metadata::JsonLoader(const JsonArgs&) {
static const auto* loader = JsonObjectLoader<Metadata>()
.OptionalField("invert", &Metadata::invert)
.Finish();
return loader;
}
//
// RbacConfig::RbacPolicy::Rules::Policy::Permission::PermissionList
//
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::Policy::Permission::PermissionList::JsonLoader(
const JsonArgs&) {
static const auto* loader = JsonObjectLoader<PermissionList>()
.Field("rules", &PermissionList::rules)
.Finish();
return loader;
}
//
// RbacConfig::RbacPolicy::Rules::Policy::Permission
//
std::vector<std::unique_ptr<Rbac::Permission>>
RbacConfig::RbacPolicy::Rules::Policy::Permission::MakeRbacPermissionList(
std::vector<Permission> permission_list) {
std::vector<std::unique_ptr<Rbac::Permission>> permissions;
permissions.reserve(permission_list.size());
for (auto& rule : permission_list) {
permissions.emplace_back(std::move(rule.permission));
}
return permissions;
}
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::Policy::Permission::JsonLoader(const JsonArgs&) {
// All fields handled in JsonPostLoad().
static const auto* loader = JsonObjectLoader<Permission>().Finish();
return loader;
}
void RbacConfig::RbacPolicy::Rules::Policy::Permission::JsonPostLoad(
const Json& json, const JsonArgs& args, ValidationErrors* errors) {
const size_t original_error_size = errors->size();
auto any = LoadJsonObjectField<bool>(json.object(), args, "any", errors,
/*required=*/false);
if (any.has_value()) {
permission = std::make_unique<Rbac::Permission>(
Rbac::Permission::MakeAnyPermission());
return;
}
auto header =
LoadJsonObjectField<HeaderMatch>(json.object(), args, "header", errors,
/*required=*/false);
if (header.has_value()) {
permission = std::make_unique<Rbac::Permission>(
Rbac::Permission::MakeHeaderPermission(std::move(header->matcher)));
return;
}
auto url_path =
LoadJsonObjectField<PathMatch>(json.object(), args, "urlPath", errors,
/*required=*/false);
if (url_path.has_value()) {
permission = std::make_unique<Rbac::Permission>(
Rbac::Permission::MakePathPermission(url_path->path.matcher));
return;
}
auto destination_ip = LoadJsonObjectField<CidrRange>(json.object(), args,
"destinationIp", errors,
/*required=*/false);
if (destination_ip.has_value()) {
permission = std::make_unique<Rbac::Permission>(
Rbac::Permission::MakeDestIpPermission(
std::move(destination_ip->cidr_range)));
return;
}
auto destination_port = LoadJsonObjectField<uint32_t>(
json.object(), args, "destinationPort", errors,
/*required=*/false);
if (destination_port.has_value()) {
permission = std::make_unique<Rbac::Permission>(
Rbac::Permission::MakeDestPortPermission(*destination_port));
return;
}
auto metadata =
LoadJsonObjectField<Metadata>(json.object(), args, "metadata", errors,
/*required=*/false);
if (metadata.has_value()) {
permission = std::make_unique<Rbac::Permission>(
Rbac::Permission::MakeMetadataPermission(metadata->invert));
return;
}
auto requested_server_name = LoadJsonObjectField<StringMatch>(
json.object(), args, "requestedServerName", errors,
/*required=*/false);
if (requested_server_name.has_value()) {
permission = std::make_unique<Rbac::Permission>(
Rbac::Permission::MakeReqServerNamePermission(
std::move(requested_server_name->matcher)));
return;
}
auto rules = LoadJsonObjectField<PermissionList>(json.object(), args,
"andRules", errors,
/*required=*/false);
if (rules.has_value()) {
permission =
std::make_unique<Rbac::Permission>(Rbac::Permission::MakeAndPermission(
MakeRbacPermissionList(std::move(rules->rules))));
return;
}
rules = LoadJsonObjectField<PermissionList>(json.object(), args, "orRules",
errors,
/*required=*/false);
if (rules.has_value()) {
permission =
std::make_unique<Rbac::Permission>(Rbac::Permission::MakeOrPermission(
MakeRbacPermissionList(std::move(rules->rules))));
return;
}
auto not_rule =
LoadJsonObjectField<Permission>(json.object(), args, "notRule", errors,
/*required=*/false);
if (not_rule.has_value()) {
permission = std::make_unique<Rbac::Permission>(
Rbac::Permission::MakeNotPermission(std::move(*not_rule->permission)));
return;
}
if (errors->size() == original_error_size) {
errors->AddError("no valid rule found");
}
}
//
// RbacConfig::RbacPolicy::Rules::Policy::Principal::PrincipalList
//
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::Policy::Principal::PrincipalList::JsonLoader(
const JsonArgs&) {
static const auto* loader = JsonObjectLoader<PrincipalList>()
.Field("ids", &PrincipalList::ids)
.Finish();
return loader;
}
//
// RbacConfig::RbacPolicy::Rules::Policy::Principal::Authenticated
//
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::Policy::Principal::Authenticated::JsonLoader(
const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<Authenticated>()
.OptionalField("principalName", &Authenticated::principal_name)
.Finish();
return loader;
}
//
// RbacConfig::RbacPolicy::Rules::Policy::Principal
//
std::vector<std::unique_ptr<Rbac::Principal>>
RbacConfig::RbacPolicy::Rules::Policy::Principal::MakeRbacPrincipalList(
std::vector<Principal> principal_list) {
std::vector<std::unique_ptr<Rbac::Principal>> principals;
principals.reserve(principal_list.size());
for (auto& id : principal_list) {
principals.emplace_back(std::move(id.principal));
}
return principals;
}
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::Policy::Principal::JsonLoader(const JsonArgs&) {
// All fields handled in JsonPostLoad().
static const auto* loader = JsonObjectLoader<Principal>().Finish();
return loader;
}
void RbacConfig::RbacPolicy::Rules::Policy::Principal::JsonPostLoad(
const Json& json, const JsonArgs& args, ValidationErrors* errors) {
const size_t original_error_size = errors->size();
auto any = LoadJsonObjectField<bool>(json.object(), args, "any", errors,
/*required=*/false);
if (any.has_value()) {
principal =
std::make_unique<Rbac::Principal>(Rbac::Principal::MakeAnyPrincipal());
return;
}
auto authenticated = LoadJsonObjectField<Authenticated>(
json.object(), args, "authenticated", errors,
/*required=*/false);
if (authenticated.has_value()) {
if (authenticated->principal_name.has_value()) {
principal = std::make_unique<Rbac::Principal>(
Rbac::Principal::MakeAuthenticatedPrincipal(
std::move(authenticated->principal_name->matcher)));
} else {
// No principalName found. Match for all users.
principal = std::make_unique<Rbac::Principal>(
Rbac::Principal::MakeAnyPrincipal());
}
return;
}
auto cidr_range =
LoadJsonObjectField<CidrRange>(json.object(), args, "sourceIp", errors,
/*required=*/false);
if (cidr_range.has_value()) {
principal = std::make_unique<Rbac::Principal>(
Rbac::Principal::MakeSourceIpPrincipal(
std::move(cidr_range->cidr_range)));
return;
}
cidr_range = LoadJsonObjectField<CidrRange>(json.object(), args,
"directRemoteIp", errors,
/*required=*/false);
if (cidr_range.has_value()) {
principal = std::make_unique<Rbac::Principal>(
Rbac::Principal::MakeDirectRemoteIpPrincipal(
std::move(cidr_range->cidr_range)));
return;
}
cidr_range =
LoadJsonObjectField<CidrRange>(json.object(), args, "remoteIp", errors,
/*required=*/false);
if (cidr_range.has_value()) {
principal = std::make_unique<Rbac::Principal>(
Rbac::Principal::MakeRemoteIpPrincipal(
std::move(cidr_range->cidr_range)));
return;
}
auto header =
LoadJsonObjectField<HeaderMatch>(json.object(), args, "header", errors,
/*required=*/false);
if (header.has_value()) {
principal = std::make_unique<Rbac::Principal>(
Rbac::Principal::MakeHeaderPrincipal(std::move(header->matcher)));
return;
}
auto url_path =
LoadJsonObjectField<PathMatch>(json.object(), args, "urlPath", errors,
/*required=*/false);
if (url_path.has_value()) {
principal = std::make_unique<Rbac::Principal>(
Rbac::Principal::MakePathPrincipal(std::move(url_path->path.matcher)));
return;
}
auto metadata =
LoadJsonObjectField<Metadata>(json.object(), args, "metadata", errors,
/*required=*/false);
if (metadata.has_value()) {
principal = std::make_unique<Rbac::Principal>(
Rbac::Principal::MakeMetadataPrincipal(metadata->invert));
return;
}
auto ids =
LoadJsonObjectField<PrincipalList>(json.object(), args, "andIds", errors,
/*required=*/false);
if (ids.has_value()) {
principal =
std::make_unique<Rbac::Principal>(Rbac::Principal::MakeAndPrincipal(
MakeRbacPrincipalList(std::move(ids->ids))));
return;
}
ids = LoadJsonObjectField<PrincipalList>(json.object(), args, "orIds", errors,
/*required=*/false);
if (ids.has_value()) {
principal =
std::make_unique<Rbac::Principal>(Rbac::Principal::MakeOrPrincipal(
MakeRbacPrincipalList(std::move(ids->ids))));
return;
}
auto not_rule =
LoadJsonObjectField<Principal>(json.object(), args, "notId", errors,
/*required=*/false);
if (not_rule.has_value()) {
principal = std::make_unique<Rbac::Principal>(
Rbac::Principal::MakeNotPrincipal(std::move(*not_rule->principal)));
return;
}
if (errors->size() == original_error_size) {
errors->AddError("no valid id found");
}
}
//
// RbacConfig::RbacPolicy::Rules::Policy
//
Rbac::Policy RbacConfig::RbacPolicy::Rules::Policy::TakeAsRbacPolicy() {
Rbac::Policy policy;
policy.permissions = Rbac::Permission::MakeOrPermission(
Permission::MakeRbacPermissionList(std::move(permissions)));
policy.principals = Rbac::Principal::MakeOrPrincipal(
Principal::MakeRbacPrincipalList(std::move(principals)));
return policy;
}
const JsonLoaderInterface* RbacConfig::RbacPolicy::Rules::Policy::JsonLoader(
const JsonArgs&) {
static const auto* loader = JsonObjectLoader<Policy>()
.Field("permissions", &Policy::permissions)
.Field("principals", &Policy::principals)
.Finish();
return loader;
}
//
// RbacConfig::RbacPolicy::Rules::AuditLogger
//
const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::AuditLogger::JsonLoader(const JsonArgs&) {
// All fields handled in JsonPostLoad().
static const auto* loader = JsonObjectLoader<AuditLogger>().Finish();
return loader;
}
void RbacConfig::RbacPolicy::Rules::AuditLogger::JsonPostLoad(
const Json& json, const JsonArgs& args, ValidationErrors* errors) {
// Should have exactly one field as the logger name.
if (json.object().size() != 1) {
errors->AddError("audit logger should have exactly one field");
return;
}
name = json.object().begin()->first;
auto config_or =
LoadJsonObjectField<Json::Object>(json.object(), args, name, errors);
if (config_or.has_value()) {
config = std::move(*config_or);
}
}
//
// RbacConfig::RbacPolicy::Rules
//
Rbac RbacConfig::RbacPolicy::Rules::TakeAsRbac(std::string name) {
Rbac rbac;
rbac.name = std::move(name);
rbac.action = static_cast<Rbac::Action>(action);
rbac.audit_condition = audit_condition;
for (auto& p : policies) {
rbac.policies.emplace(p.first, p.second.TakeAsRbacPolicy());
}
rbac.logger_configs = std::move(logger_configs);
return rbac;
}
const JsonLoaderInterface* RbacConfig::RbacPolicy::Rules::JsonLoader(
const JsonArgs&) {
// Audit logger configs handled in post load.
static const auto* loader = JsonObjectLoader<Rules>()
.Field("action", &Rules::action)
.OptionalField("policies", &Rules::policies)
.Finish();
return loader;
}
void RbacConfig::RbacPolicy::Rules::JsonPostLoad(const Json& json,
const JsonArgs& args,
ValidationErrors* errors) {
// Validate action field.
auto rbac_action = static_cast<Rbac::Action>(action);
if (rbac_action != Rbac::Action::kAllow &&
rbac_action != Rbac::Action::kDeny) {
ValidationErrors::ScopedField field(errors, ".action");
errors->AddError("unknown action");
}
// Parse and validate audit_condition field.
auto condition = LoadJsonObjectField<int>(json.object(), args,
"audit_condition", errors, false);
if (condition.has_value()) {
switch (*condition) {
case static_cast<int>(Rbac::AuditCondition::kNone):
case static_cast<int>(Rbac::AuditCondition::kOnAllow):
case static_cast<int>(Rbac::AuditCondition::kOnDeny):
case static_cast<int>(Rbac::AuditCondition::kOnDenyAndAllow):
audit_condition = static_cast<Rbac::AuditCondition>(*condition);
break;
default: {
ValidationErrors::ScopedField field(errors, ".audit_condition");
errors->AddError("unknown audit condition");
}
}
}
// Parse and validate audit logger configs.
auto configs = LoadJsonObjectField<std::vector<AuditLogger>>(
json.object(), args, "audit_loggers", errors, false);
if (configs.has_value()) {
for (size_t i = 0; i < configs->size(); ++i) {
auto& logger = (*configs)[i];
auto config = AuditLoggerRegistry::ParseConfig(
logger.name, Json::FromObject(std::move(logger.config)));
if (!config.ok()) {
ValidationErrors::ScopedField field(
errors, absl::StrCat(".audit_loggers[", i, "]"));
errors->AddError(config.status().message());
continue;
}
logger_configs.push_back(std::move(*config));
}
}
}
//
// RbacConfig::RbacPolicy
//
Rbac RbacConfig::RbacPolicy::TakeAsRbac() {
if (!rules.has_value()) {
// No enforcing to be applied. An empty deny policy with an empty map
// is equivalent to no enforcing.
return Rbac(std::move(name), Rbac::Action::kDeny, {});
}
return rules->TakeAsRbac(std::move(name));
}
const JsonLoaderInterface* RbacConfig::RbacPolicy::JsonLoader(const JsonArgs&) {
static const auto* loader = JsonObjectLoader<RbacPolicy>()
.OptionalField("rules", &RbacPolicy::rules)
.Field("filter_name", &RbacPolicy::name)
.Finish();
return loader;
}
//
// RbacConfig
//
std::vector<Rbac> RbacConfig::TakeAsRbacList() {
std::vector<Rbac> rbac_list;
rbac_list.reserve(rbac_policies.size());
for (auto& rbac_policy : rbac_policies) {
rbac_list.emplace_back(rbac_policy.TakeAsRbac());
}
return rbac_list;
}
const JsonLoaderInterface* RbacConfig::JsonLoader(const JsonArgs&) {
static const auto* loader =
JsonObjectLoader<RbacConfig>()
.Field("rbacPolicy", &RbacConfig::rbac_policies)
.Finish();
return loader;
}
} // namespace
std::unique_ptr<ServiceConfigParser::ParsedConfig>
RbacServiceConfigParser::ParsePerMethodParams(const ChannelArgs& args,
const Json& json,
ValidationErrors* errors) {
// Only parse rbac policy if the channel arg is present
if (!args.GetBool(GRPC_ARG_PARSE_RBAC_METHOD_CONFIG).value_or(false)) {
return nullptr;
}
auto rbac_config = LoadFromJson<RbacConfig>(json, JsonArgs(), errors);
std::vector<Rbac> rbac_policies = rbac_config.TakeAsRbacList();
if (rbac_policies.empty()) return nullptr;
return std::make_unique<RbacMethodParsedConfig>(std::move(rbac_policies));
}
void RbacServiceConfigParser::Register(CoreConfiguration::Builder* builder) {
builder->service_config_parser()->RegisterParser(
std::make_unique<RbacServiceConfigParser>());
}
size_t RbacServiceConfigParser::ParserIndex() {
return CoreConfiguration::Get().service_config_parser().GetParserIndex(
parser_name());
}
} // namespace grpc_core