| // |
| // 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 |