blob: 49508a9428a94e7134ae007eb8b1e8559fefb75c [file] [log] [blame] [edit]
#include "resource_authz.h"
#include <algorithm>
#include <cstddef>
#include <string>
#include <vector>
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "grpcpp/security/auth_context.h"
#include "metrics.h"
#include "proxy_config.pb.h"
#include "redfish_plugin.h"
namespace milotic {
static bool RequestVerbMatches(milotic_grpc_proxy::RequestVerb proto_verb,
RedfishPlugin::RequestVerb verb) {
switch (proto_verb) {
case milotic_grpc_proxy::REQUEST_VERB_UNSPECIFIED:
return true;
case milotic_grpc_proxy::REQUEST_VERB_GET:
return verb == RedfishPlugin::RequestVerb::kGet;
case milotic_grpc_proxy::REQUEST_VERB_POST:
return verb == RedfishPlugin::RequestVerb::kPost;
case milotic_grpc_proxy::REQUEST_VERB_PUT:
return verb == RedfishPlugin::RequestVerb::kPut;
case milotic_grpc_proxy::REQUEST_VERB_PATCH:
return verb == RedfishPlugin::RequestVerb::kPatch;
case milotic_grpc_proxy::REQUEST_VERB_DELETE:
return verb == RedfishPlugin::RequestVerb::kDelete;
case milotic_grpc_proxy::REQUEST_VERB_SUBSCRIBE:
return verb == RedfishPlugin::RequestVerb::kSubscribe;
case milotic_grpc_proxy::REQUEST_VERB_BIDIRECTIONAL_STREAM:
return verb == RedfishPlugin::RequestVerb::kBidirectionalStream;
default:
return false;
}
}
static bool RuleApplies(
const milotic_grpc_proxy::AuthorizationPolicy::Rule& rule,
AuthorizationContext& context) {
switch (rule.permission_case()) {
case milotic_grpc_proxy::AuthorizationPolicy::Rule::kPermissionId:
if (context.permission_checker == nullptr) {
LOG(ERROR) << "Permission checker is null";
return false;
}
return context.permission_checker->Check(context, rule.permission_id());
case milotic_grpc_proxy::AuthorizationPolicy::Rule::kLocal: {
auto values =
context.grpc_context.FindPropertyValues("transport_security_type");
return std::find(values.begin(), values.end(), "local") != values.end();
}
case milotic_grpc_proxy::AuthorizationPolicy::Rule::PERMISSION_NOT_SET:
return true;
}
return false;
}
static bool ResourceMatches(absl::string_view expected, bool with_subtree,
absl::string_view redfish_id) {
if (expected.ends_with('/')) {
expected.remove_suffix(1);
}
// Match required
if (!redfish_id.starts_with(expected)) return false;
// Prefix matches
if (redfish_id.ends_with('/')) {
redfish_id.remove_suffix(1);
}
if (redfish_id.size() == expected.size()) return true;
// Contains suffix
if (redfish_id[expected.size()] != '/' && redfish_id[expected.size()] != '?')
return false;
// Suffix is subtree or query params
if (with_subtree) return true;
// Subtree should not match
size_t params_pos = redfish_id.find('?');
if (params_pos == absl::string_view::npos) return false;
// Has query params
if (redfish_id.find("$expand=", params_pos) != absl::string_view::npos) {
return false;
}
// No expand in query params
redfish_id.remove_suffix(redfish_id.size() - params_pos);
if (redfish_id.ends_with('/')) {
redfish_id.remove_suffix(1);
}
// Query params removed
return redfish_id.size() == expected.size();
}
absl::Status AuthorizeRequest(
const milotic_grpc_proxy::AuthorizationPolicy& authorization_policy,
RedfishPlugin::RequestVerb verb, absl::string_view redfish_id,
AuthorizationContext& auth_context) {
using Mapping = milotic_grpc_proxy::AuthorizationPolicy::Mapping;
for (const Mapping& mapping : authorization_policy.mappings()) {
if (!RequestVerbMatches(mapping.request_verb(), verb)) continue;
if (!std::any_of(mapping.resource_path().begin(),
mapping.resource_path().end(),
[redfish_id, &mapping](absl::string_view resource_path) {
return ResourceMatches(
resource_path, mapping.with_subtree(), redfish_id);
}))
continue;
CommonMetrics::Get().mapping_counter.Increment(mapping.name());
for (const auto& [name, rule] : mapping.allow()) {
bool ok = RuleApplies(rule, auth_context);
CommonMetrics::Get().rule_counter.Increment({name, ok});
if (ok) {
return absl::OkStatus();
}
}
return absl::PermissionDeniedError(
absl::StrFormat("Permission denied for %s %s using mapping %s",
VerbToString(verb), redfish_id, mapping.name()));
}
return absl::PermissionDeniedError(
absl::StrFormat("Permission denied: No maching rule found for %s %s",
VerbToString(verb), redfish_id));
}
} // namespace milotic