|
|
|
@ -111,16 +111,20 @@ absl::StatusOr<Rbac::Principal> ParsePrincipalsArray(const Json& json) { |
|
|
|
|
|
|
|
|
|
absl::StatusOr<Rbac::Principal> ParsePeer(const Json& json) { |
|
|
|
|
std::vector<std::unique_ptr<Rbac::Principal>> peer; |
|
|
|
|
auto it = json.object_value().find("principals"); |
|
|
|
|
if (it != json.object_value().end()) { |
|
|
|
|
if (it->second.type() != Json::Type::ARRAY) { |
|
|
|
|
return absl::InvalidArgumentError("\"principals\" is not an array."); |
|
|
|
|
} |
|
|
|
|
auto principal_names_or = ParsePrincipalsArray(it->second); |
|
|
|
|
if (!principal_names_or.ok()) return principal_names_or.status(); |
|
|
|
|
if (!principal_names_or.value().principals.empty()) { |
|
|
|
|
peer.push_back(std::make_unique<Rbac::Principal>( |
|
|
|
|
std::move(principal_names_or.value()))); |
|
|
|
|
for (const auto& object : json.object_value()) { |
|
|
|
|
if (object.first == "principals") { |
|
|
|
|
if (object.second.type() != Json::Type::ARRAY) { |
|
|
|
|
return absl::InvalidArgumentError("\"principals\" is not an array."); |
|
|
|
|
} |
|
|
|
|
auto principal_names_or = ParsePrincipalsArray(object.second); |
|
|
|
|
if (!principal_names_or.ok()) return principal_names_or.status(); |
|
|
|
|
if (!principal_names_or.value().principals.empty()) { |
|
|
|
|
peer.push_back(std::make_unique<Rbac::Principal>( |
|
|
|
|
std::move(principal_names_or.value()))); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
return absl::InvalidArgumentError(absl::StrFormat( |
|
|
|
|
"policy contains unknown field \"%s\" in \"source\".", object.first)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (peer.empty()) { |
|
|
|
@ -154,28 +158,36 @@ absl::StatusOr<Rbac::Permission> ParseHeaderValues( |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
absl::StatusOr<Rbac::Permission> ParseHeaders(const Json& json) { |
|
|
|
|
auto it = json.object_value().find("key"); |
|
|
|
|
if (it == json.object_value().end()) { |
|
|
|
|
return absl::InvalidArgumentError("\"key\" is not present."); |
|
|
|
|
} |
|
|
|
|
if (it->second.type() != Json::Type::STRING) { |
|
|
|
|
return absl::InvalidArgumentError("\"key\" is not a string."); |
|
|
|
|
absl::string_view key; |
|
|
|
|
const Json* values = nullptr; |
|
|
|
|
for (const auto& object : json.object_value()) { |
|
|
|
|
if (object.first == "key") { |
|
|
|
|
if (object.second.type() != Json::Type::STRING) { |
|
|
|
|
return absl::InvalidArgumentError("\"key\" is not a string."); |
|
|
|
|
} |
|
|
|
|
key = object.second.string_value(); |
|
|
|
|
if (absl::StartsWith(key, ":") || absl::StartsWith(key, "grpc-") || |
|
|
|
|
IsUnsupportedHeader(key)) { |
|
|
|
|
return absl::InvalidArgumentError( |
|
|
|
|
absl::StrFormat("Unsupported \"key\" %s.", key)); |
|
|
|
|
} |
|
|
|
|
} else if (object.first == "values") { |
|
|
|
|
if (object.second.type() != Json::Type::ARRAY) { |
|
|
|
|
return absl::InvalidArgumentError("\"values\" is not an array."); |
|
|
|
|
} |
|
|
|
|
values = &object.second; |
|
|
|
|
} else { |
|
|
|
|
return absl::InvalidArgumentError(absl::StrFormat( |
|
|
|
|
"policy contains unknown field \"%s\".", object.first)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
absl::string_view header_name = it->second.string_value(); |
|
|
|
|
if (absl::StartsWith(header_name, ":") || |
|
|
|
|
absl::StartsWith(header_name, "grpc-") || |
|
|
|
|
IsUnsupportedHeader(header_name)) { |
|
|
|
|
return absl::InvalidArgumentError( |
|
|
|
|
absl::StrFormat("Unsupported \"key\" %s.", header_name)); |
|
|
|
|
if (key.empty()) { |
|
|
|
|
return absl::InvalidArgumentError("\"key\" is not present."); |
|
|
|
|
} |
|
|
|
|
it = json.object_value().find("values"); |
|
|
|
|
if (it == json.object_value().end()) { |
|
|
|
|
if (values == nullptr) { |
|
|
|
|
return absl::InvalidArgumentError("\"values\" is not present."); |
|
|
|
|
} |
|
|
|
|
if (it->second.type() != Json::Type::ARRAY) { |
|
|
|
|
return absl::InvalidArgumentError("\"values\" is not an array."); |
|
|
|
|
} |
|
|
|
|
return ParseHeaderValues(it->second, header_name); |
|
|
|
|
return ParseHeaderValues(*values, key); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
absl::StatusOr<Rbac::Permission> ParseHeadersArray(const Json& json) { |
|
|
|
@ -220,28 +232,31 @@ absl::StatusOr<Rbac::Permission> ParsePathsArray(const Json& json) { |
|
|
|
|
|
|
|
|
|
absl::StatusOr<Rbac::Permission> ParseRequest(const Json& json) { |
|
|
|
|
std::vector<std::unique_ptr<Rbac::Permission>> request; |
|
|
|
|
auto it = json.object_value().find("paths"); |
|
|
|
|
if (it != json.object_value().end()) { |
|
|
|
|
if (it->second.type() != Json::Type::ARRAY) { |
|
|
|
|
return absl::InvalidArgumentError("\"paths\" is not an array."); |
|
|
|
|
} |
|
|
|
|
auto paths_or = ParsePathsArray(it->second); |
|
|
|
|
if (!paths_or.ok()) return paths_or.status(); |
|
|
|
|
if (!paths_or.value().permissions.empty()) { |
|
|
|
|
request.push_back( |
|
|
|
|
std::make_unique<Rbac::Permission>(std::move(paths_or.value()))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
it = json.object_value().find("headers"); |
|
|
|
|
if (it != json.object_value().end()) { |
|
|
|
|
if (it->second.type() != Json::Type::ARRAY) { |
|
|
|
|
return absl::InvalidArgumentError("\"headers\" is not an array."); |
|
|
|
|
} |
|
|
|
|
auto headers_or = ParseHeadersArray(it->second); |
|
|
|
|
if (!headers_or.ok()) return headers_or.status(); |
|
|
|
|
if (!headers_or.value().permissions.empty()) { |
|
|
|
|
request.push_back( |
|
|
|
|
std::make_unique<Rbac::Permission>(std::move(headers_or.value()))); |
|
|
|
|
for (const auto& object : json.object_value()) { |
|
|
|
|
if (object.first == "paths") { |
|
|
|
|
if (object.second.type() != Json::Type::ARRAY) { |
|
|
|
|
return absl::InvalidArgumentError("\"paths\" is not an array."); |
|
|
|
|
} |
|
|
|
|
auto paths_or = ParsePathsArray(object.second); |
|
|
|
|
if (!paths_or.ok()) return paths_or.status(); |
|
|
|
|
if (!paths_or.value().permissions.empty()) { |
|
|
|
|
request.push_back( |
|
|
|
|
std::make_unique<Rbac::Permission>(std::move(paths_or.value()))); |
|
|
|
|
} |
|
|
|
|
} else if (object.first == "headers") { |
|
|
|
|
if (object.second.type() != Json::Type::ARRAY) { |
|
|
|
|
return absl::InvalidArgumentError("\"headers\" is not an array."); |
|
|
|
|
} |
|
|
|
|
auto headers_or = ParseHeadersArray(object.second); |
|
|
|
|
if (!headers_or.ok()) return headers_or.status(); |
|
|
|
|
if (!headers_or.value().permissions.empty()) { |
|
|
|
|
request.push_back( |
|
|
|
|
std::make_unique<Rbac::Permission>(std::move(headers_or.value()))); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
return absl::InvalidArgumentError(absl::StrFormat( |
|
|
|
|
"policy contains unknown field \"%s\" in \"request\".", |
|
|
|
|
object.first)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (request.empty()) { |
|
|
|
@ -250,36 +265,53 @@ absl::StatusOr<Rbac::Permission> ParseRequest(const Json& json) { |
|
|
|
|
return Rbac::Permission::MakeAndPermission(std::move(request)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
absl::StatusOr<Rbac::Policy> ParseRules(const Json& json) { |
|
|
|
|
Rbac::Principal principals; |
|
|
|
|
auto it = json.object_value().find("source"); |
|
|
|
|
if (it != json.object_value().end()) { |
|
|
|
|
if (it->second.type() != Json::Type::OBJECT) { |
|
|
|
|
return absl::InvalidArgumentError("\"source\" is not an object."); |
|
|
|
|
absl::StatusOr<Rbac::Policy> ParseRule(const Json& json, |
|
|
|
|
std::string* policy_name) { |
|
|
|
|
absl::optional<Rbac::Principal> principals; |
|
|
|
|
absl::optional<Rbac::Permission> permissions; |
|
|
|
|
for (const auto& object : json.object_value()) { |
|
|
|
|
if (object.first == "name") { |
|
|
|
|
if (object.second.type() != Json::Type::STRING) { |
|
|
|
|
return absl::InvalidArgumentError( |
|
|
|
|
absl::StrCat("\"name\" is not a string.")); |
|
|
|
|
} |
|
|
|
|
*policy_name = object.second.string_value(); |
|
|
|
|
} else if (object.first == "source") { |
|
|
|
|
if (object.second.type() != Json::Type::OBJECT) { |
|
|
|
|
return absl::InvalidArgumentError("\"source\" is not an object."); |
|
|
|
|
} |
|
|
|
|
auto peer_or = ParsePeer(object.second); |
|
|
|
|
if (!peer_or.ok()) return peer_or.status(); |
|
|
|
|
principals = std::move(*peer_or); |
|
|
|
|
} else if (object.first == "request") { |
|
|
|
|
if (object.second.type() != Json::Type::OBJECT) { |
|
|
|
|
return absl::InvalidArgumentError("\"request\" is not an object."); |
|
|
|
|
} |
|
|
|
|
auto request_or = ParseRequest(object.second); |
|
|
|
|
if (!request_or.ok()) return request_or.status(); |
|
|
|
|
permissions = std::move(*request_or); |
|
|
|
|
} else { |
|
|
|
|
return absl::InvalidArgumentError(absl::StrFormat( |
|
|
|
|
"policy contains unknown field \"%s\" in \"rule\".", object.first)); |
|
|
|
|
} |
|
|
|
|
auto peer_or = ParsePeer(it->second); |
|
|
|
|
if (!peer_or.ok()) return peer_or.status(); |
|
|
|
|
principals = std::move(peer_or.value()); |
|
|
|
|
} else { |
|
|
|
|
} |
|
|
|
|
if (policy_name->empty()) { |
|
|
|
|
return absl::InvalidArgumentError(absl::StrCat("\"name\" is not present.")); |
|
|
|
|
} |
|
|
|
|
if (!principals.has_value()) { |
|
|
|
|
principals = Rbac::Principal::MakeAnyPrincipal(); |
|
|
|
|
} |
|
|
|
|
Rbac::Permission permissions; |
|
|
|
|
it = json.object_value().find("request"); |
|
|
|
|
if (it != json.object_value().end()) { |
|
|
|
|
if (it->second.type() != Json::Type::OBJECT) { |
|
|
|
|
return absl::InvalidArgumentError("\"request\" is not an object."); |
|
|
|
|
} |
|
|
|
|
auto request_or = ParseRequest(it->second); |
|
|
|
|
if (!request_or.ok()) return request_or.status(); |
|
|
|
|
permissions = std::move(request_or.value()); |
|
|
|
|
} else { |
|
|
|
|
if (!permissions.has_value()) { |
|
|
|
|
permissions = Rbac::Permission::MakeAnyPermission(); |
|
|
|
|
} |
|
|
|
|
return Rbac::Policy(std::move(permissions), std::move(principals)); |
|
|
|
|
return Rbac::Policy(std::move(*permissions), std::move(*principals)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
absl::StatusOr<std::map<std::string, Rbac::Policy>> ParseRulesArray( |
|
|
|
|
const Json& json, absl::string_view name) { |
|
|
|
|
if (json.array_value().empty()) { |
|
|
|
|
return absl::InvalidArgumentError("rules is empty."); |
|
|
|
|
} |
|
|
|
|
std::map<std::string, Rbac::Policy> policies; |
|
|
|
|
for (size_t i = 0; i < json.array_value().size(); ++i) { |
|
|
|
|
const Json& child = json.array_value().at(i); |
|
|
|
@ -287,24 +319,15 @@ absl::StatusOr<std::map<std::string, Rbac::Policy>> ParseRulesArray( |
|
|
|
|
return absl::InvalidArgumentError( |
|
|
|
|
absl::StrCat("rules ", i, ": is not an object.")); |
|
|
|
|
} |
|
|
|
|
auto it = child.object_value().find("name"); |
|
|
|
|
if (it == child.object_value().end()) { |
|
|
|
|
return absl::InvalidArgumentError( |
|
|
|
|
absl::StrCat("rules ", i, ": \"name\" is not present.")); |
|
|
|
|
} |
|
|
|
|
if (it->second.type() != Json::Type::STRING) { |
|
|
|
|
return absl::InvalidArgumentError( |
|
|
|
|
absl::StrCat("rules ", i, ": \"name\" is not a string.")); |
|
|
|
|
} |
|
|
|
|
std::string policy_name = |
|
|
|
|
std::string(name) + "_" + it->second.string_value(); |
|
|
|
|
auto policy_or = ParseRules(child); |
|
|
|
|
std::string policy_name; |
|
|
|
|
auto policy_or = ParseRule(child, &policy_name); |
|
|
|
|
if (!policy_or.ok()) { |
|
|
|
|
return absl::Status( |
|
|
|
|
policy_or.status().code(), |
|
|
|
|
absl::StrCat("rules ", i, ": ", policy_or.status().message())); |
|
|
|
|
} |
|
|
|
|
policies[policy_name] = std::move(policy_or.value()); |
|
|
|
|
policies[std::string(name) + "_" + policy_name] = |
|
|
|
|
std::move(policy_or.value()); |
|
|
|
|
} |
|
|
|
|
return std::move(policies); |
|
|
|
|
} |
|
|
|
@ -337,45 +360,51 @@ absl::StatusOr<RbacPolicies> GenerateRbacPolicies( |
|
|
|
|
return absl::InvalidArgumentError( |
|
|
|
|
"SDK authorization policy is not an object."); |
|
|
|
|
} |
|
|
|
|
auto it = json->mutable_object()->find("name"); |
|
|
|
|
if (it == json->mutable_object()->end()) { |
|
|
|
|
auto it = json->object_value().find("name"); |
|
|
|
|
if (it == json->object_value().end()) { |
|
|
|
|
return absl::InvalidArgumentError("\"name\" field is not present."); |
|
|
|
|
} |
|
|
|
|
if (it->second.type() != Json::Type::STRING) { |
|
|
|
|
return absl::InvalidArgumentError("\"name\" is not a string."); |
|
|
|
|
} |
|
|
|
|
absl::string_view name = it->second.string_value(); |
|
|
|
|
RbacPolicies rbac_policies; |
|
|
|
|
it = json->mutable_object()->find("deny_rules"); |
|
|
|
|
if (it != json->mutable_object()->end()) { |
|
|
|
|
if (it->second.type() != Json::Type::ARRAY) { |
|
|
|
|
return absl::InvalidArgumentError("\"deny_rules\" is not an array."); |
|
|
|
|
} |
|
|
|
|
auto deny_policy_or = ParseDenyRulesArray(it->second, name); |
|
|
|
|
if (!deny_policy_or.ok()) { |
|
|
|
|
return absl::Status( |
|
|
|
|
deny_policy_or.status().code(), |
|
|
|
|
absl::StrCat("deny_", deny_policy_or.status().message())); |
|
|
|
|
RbacPolicies rbacs; |
|
|
|
|
bool has_allow_rbac = false; |
|
|
|
|
for (const auto& object : json->object_value()) { |
|
|
|
|
if (object.first == "name") { |
|
|
|
|
continue; |
|
|
|
|
} else if (object.first == "deny_rules") { |
|
|
|
|
if (object.second.type() != Json::Type::ARRAY) { |
|
|
|
|
return absl::InvalidArgumentError("\"deny_rules\" is not an array."); |
|
|
|
|
} |
|
|
|
|
auto deny_policy_or = ParseDenyRulesArray(object.second, name); |
|
|
|
|
if (!deny_policy_or.ok()) { |
|
|
|
|
return absl::Status( |
|
|
|
|
deny_policy_or.status().code(), |
|
|
|
|
absl::StrCat("deny_", deny_policy_or.status().message())); |
|
|
|
|
} |
|
|
|
|
rbacs.deny_policy = std::move(*deny_policy_or); |
|
|
|
|
} else if (object.first == "allow_rules") { |
|
|
|
|
if (object.second.type() != Json::Type::ARRAY) { |
|
|
|
|
return absl::InvalidArgumentError("\"allow_rules\" is not an array."); |
|
|
|
|
} |
|
|
|
|
auto allow_policy_or = ParseAllowRulesArray(object.second, name); |
|
|
|
|
if (!allow_policy_or.ok()) { |
|
|
|
|
return absl::Status( |
|
|
|
|
allow_policy_or.status().code(), |
|
|
|
|
absl::StrCat("allow_", allow_policy_or.status().message())); |
|
|
|
|
} |
|
|
|
|
rbacs.allow_policy = std::move(*allow_policy_or); |
|
|
|
|
has_allow_rbac = true; |
|
|
|
|
} else { |
|
|
|
|
return absl::InvalidArgumentError(absl::StrFormat( |
|
|
|
|
"policy contains unknown field \"%s\".", object.first)); |
|
|
|
|
} |
|
|
|
|
rbac_policies.deny_policy = std::move(deny_policy_or.value()); |
|
|
|
|
} else { |
|
|
|
|
rbac_policies.deny_policy.action = Rbac::Action::kDeny; |
|
|
|
|
} |
|
|
|
|
it = json->mutable_object()->find("allow_rules"); |
|
|
|
|
if (it == json->mutable_object()->end()) { |
|
|
|
|
if (!has_allow_rbac) { |
|
|
|
|
return absl::InvalidArgumentError("\"allow_rules\" is not present."); |
|
|
|
|
} |
|
|
|
|
if (it->second.type() != Json::Type::ARRAY) { |
|
|
|
|
return absl::InvalidArgumentError("\"allow_rules\" is not an array."); |
|
|
|
|
} |
|
|
|
|
auto allow_policy_or = ParseAllowRulesArray(it->second, name); |
|
|
|
|
if (!allow_policy_or.ok()) { |
|
|
|
|
return absl::Status( |
|
|
|
|
allow_policy_or.status().code(), |
|
|
|
|
absl::StrCat("allow_", allow_policy_or.status().message())); |
|
|
|
|
} |
|
|
|
|
rbac_policies.allow_policy = std::move(allow_policy_or.value()); |
|
|
|
|
return std::move(rbac_policies); |
|
|
|
|
return std::move(rbacs); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace grpc_core
|
|
|
|
|