blob: 84e08086b72fb96e30840bd6d2300210e506a291 [file] [log] [blame]
#include "redfish_authorizer.h"
#include <cstddef>
#include <cstdint>
#include <fstream>
#include <iterator>
#include <limits>
#include <memory>
#include <string>
#include <string_view>
#include <unordered_set>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/synchronization/mutex.h"
#include "authorizer_enums.h"
#include "nlohmann/json.hpp"
#include "nlohmann/json_fwd.hpp"
#include "config_parser.h"
#include "json_utils.h"
#include "override.h"
#include "pattern_to_entity_array.h"
#include "redfish_entity_trie.h"
#include "redfish_privileges.h"
#include "resource_uri_override.h"
#include "subordinate_override.h"
namespace milotic::authz {
using ::ecclesia::Operation;
using ::ecclesia::ResourceEntity;
using ::ecclesia::StringToOperation;
using ::ecclesia::StringToResourceEntity;
RedfishAuthorizer::RedfishAuthorizer()
: RedfishAuthorizer(AuthorizerOptions()) {}
RedfishAuthorizer::RedfishAuthorizer(const AuthorizerOptions& options)
: options_(options),
authz_configuration_(nlohmann::json(), options.offline_node_entities_path,
options.google_machine_identity_path) {
ResetEntityPrivilegesMappings();
LoadRedfishEntityTrie();
ReloadConfiguration();
}
void RedfishAuthorizer::LoadRedfishEntityTrie() {
for (std::size_t i = 0; i < pattern_entity_pair_array.size(); ++i) {
const auto& [pattern, entity] = pattern_entity_pair_array[i];
redfish_entity_trie_.InsertUri(pattern, entity, i);
}
// Now loads non-standard URI pattern into the trie
std::ifstream configuration_file(options_.pattern_entity_overrides_path);
if (!configuration_file.is_open()) {
LOG(WARNING) << "Pattern entity overrides file at '"
<< options_.pattern_entity_overrides_path << "' is missing.";
return;
}
std::string configuration = {
std::istreambuf_iterator<char>(configuration_file),
std::istreambuf_iterator<char>()};
constexpr bool kAllowException = false;
nlohmann::json configuration_json =
nlohmann::json::parse(configuration, nullptr, kAllowException);
if (configuration_json.is_discarded()) {
LOG(WARNING) << "Pattern entity overrides file at '"
<< options_.pattern_entity_overrides_path
<< "' is not a JSON document.";
return;
}
const nlohmann::json::array_t* pattern_entity_pairs =
GetValueAsArray(configuration_json, "pattern_entity_mappings_override");
if (pattern_entity_pairs == nullptr) {
LOG(WARNING) << "There are no pattern entity mappings.";
return;
}
for (const nlohmann::json& pattern_entity_pair : *pattern_entity_pairs) {
const std::string* pattern =
GetValueAsString(pattern_entity_pair, "pattern");
const std::string* entity_str =
GetValueAsString(pattern_entity_pair, "resource");
if (pattern == nullptr || entity_str == nullptr) {
LOG(WARNING) << "Pattern entity pair is invalid.";
return;
}
ResourceEntity entity = StringToResourceEntity(*entity_str);
// TODO(nanzhou): support indexing non-standard URI patterns
redfish_entity_trie_.InsertUri(*pattern, entity,
std::numeric_limits<std::size_t>::max());
}
}
ResourceEntity RedfishAuthorizer::GetEntityTypeFromRedfishUri(
std::string_view uri) const {
return redfish_entity_trie_.GetEntityType(uri);
}
RedfishAuthorizer::RedfishAuthorizer(const nlohmann::json& registry_json,
const nlohmann::json& configuration_json) {
ResetEntityPrivilegesMappings();
LoadRedfishEntityTrie();
ReconstructPrivilegeRegistry(
GetValueAsArray(registry_json, "Mappings"),
GetValueAsArray(configuration_json, "oem_privilege_mappings"),
GetValueAsJson(configuration_json, "additional_override_mappings"),
GetValueAsArray(registry_json, "AdditionalSubtreeMappings"),
/*platform_subtree_mappings=*/nullptr);
}
static nlohmann::json ParseJsonConfig(const std::string& configuration_path) {
std::ifstream configuration_file(configuration_path);
if (!configuration_file.is_open()) {
LOG(WARNING) << "Configuration file at '" << configuration_path
<< "' is missing.";
return nlohmann::json();
}
std::string configuration = {
std::istreambuf_iterator<char>(configuration_file),
std::istreambuf_iterator<char>()};
constexpr bool kAllowException = false;
nlohmann::json configuration_json =
nlohmann::json::parse(configuration, nullptr, kAllowException);
if (configuration_json.is_discarded()) {
LOG(WARNING) << "Configuration file at '" << configuration_path
<< "' is not a JSON document.";
return nlohmann::json();
}
return configuration_json;
}
nlohmann::json RedfishAuthorizer::ParseAuthConfig() const {
return ParseJsonConfig(options_.configuration_path);
}
void RedfishAuthorizer::ParseSubscriptionLimit(
const nlohmann::json& configuration_json) {
const int64_t* subscription_limit =
GetValueAsInt(configuration_json, "universal_subscription_limit");
if (subscription_limit == nullptr) {
LOG(WARNING) << "Subscription limit not defined in Authorization Config; a "
"default value 1 will be used";
return;
}
if (*subscription_limit < 1) {
LOG(WARNING) << "Subscription shouldn't be less than 1; a default value 1 "
"will be used";
return;
}
subscription_tracker_.SetUniversalSubscriptionLimit(*subscription_limit);
}
std::string RedfishAuthorizer::FindBasePrivilegeRegistryPath(
const nlohmann::json& configuration_json) const {
const std::string* base_privilege_registry_str =
GetValueAsString(configuration_json, "base_privilege_registry");
if (base_privilege_registry_str == nullptr) {
LOG(WARNING)
<< "Base Privilege Registry not defined in Authorization Config";
return "";
}
std::string base_privilege_registry_path =
options_.base_privileges_folder + "/" + *base_privilege_registry_str;
std::ifstream registry_file(base_privilege_registry_path);
if (!registry_file.is_open()) {
LOG(WARNING) << "Base registry file at '" << base_privilege_registry_path
<< "' is missing.";
return "";
}
return base_privilege_registry_path;
}
std::string RedfishAuthorizer::GetPeerRedfishRole(
const PeerSpiffeIdentity& peer) const {
return authz_configuration_.GetPeerRedfishRole(peer);
}
uint64_t RedfishAuthorizer::GetSampleRateLimit(
const std::string& peer_role) const {
return authz_configuration_.GetSampleRateLimit(peer_role);
}
void RedfishAuthorizer::ReloadConfiguration() {
ReloadConfiguration(options_.configuration_path);
}
void RedfishAuthorizer::ReloadConfiguration(
const std::string& configuration_path) {
nlohmann::json configuration_json = ParseJsonConfig(configuration_path);
if (configuration_json.empty()) {
return;
}
// Load subscription limit
ParseSubscriptionLimit(configuration_json);
nlohmann::json platform_configuration_json =
ParseJsonConfig(options_.platform_configuration_path);
const std::string* platform_name_str =
GetValueAsString(platform_configuration_json, "name");
if (platform_name_str != nullptr) {
LOG(WARNING) << "Loading platform configuration '" << *platform_name_str
<< "'" << " from " << options_.platform_configuration_path;
}
// Load authz config
authz_configuration_.ReloadConfig(configuration_json,
platform_configuration_json);
std::string base_privilege_registry_path =
FindBasePrivilegeRegistryPath(configuration_json);
if (base_privilege_registry_path.empty()) {
return;
}
ResetEntityPrivilegesMappings();
ReconstructPrivilegeRegistry(
base_privilege_registry_path,
GetValueAsArray(configuration_json, "oem_privilege_mappings"),
GetValueAsJson(configuration_json, "additional_override_mappings"),
platform_configuration_json);
}
void RedfishAuthorizer::SetBasePrivilegesFolder(
std::string_view base_privileges_folder) {
options_.base_privileges_folder = base_privileges_folder;
ReloadConfiguration();
}
void RedfishAuthorizer::SetPrivileges(
EntityPrivilegesMappings&& new_entity_privileges_mappings,
OverrideMappings&& new_override_mappings) {
absl::MutexLock lock(&mutex_);
entity_privileges_mappings_ = std::move(new_entity_privileges_mappings);
override_mappings_ = std::move(new_override_mappings);
}
RedfishAuthorizer::EntityPrivilegesMappings
RedfishAuthorizer::DefaultEntityPrivilegesMappings() {
return EntityPrivilegesMappings(
static_cast<int>(ResourceEntity::kUndefined) + 1,
std::vector<std::vector<RedfishPrivileges>>(
static_cast<int>(Operation::kUndefined) + 1));
}
RedfishAuthorizer::OverrideMappings
RedfishAuthorizer::DefaultOverrideMappings() {
return OverrideMappings(static_cast<int>(ResourceEntity::kUndefined) + 1);
}
void RedfishAuthorizer::ResetEntityPrivilegesMappings() {
absl::MutexLock lock(&mutex_);
entity_privileges_mappings_ = DefaultEntityPrivilegesMappings();
override_mappings_ = DefaultOverrideMappings();
}
std::vector<RedfishPrivileges> RedfishAuthorizer::GetPrivilegesArrFromJsonArr(
const nlohmann::json::array_t& operation_privilege_arr_json) const {
std::vector<RedfishPrivileges> privileges_or_arr;
for (const nlohmann::json& operation_privilege :
operation_privilege_arr_json) {
RedfishPrivileges redfish_privileges;
const nlohmann::json::array_t* privilege_arr =
GetValueAsArray(operation_privilege, "Privilege");
if (privilege_arr == nullptr) {
continue;
}
for (const nlohmann::json& privilege : *privilege_arr) {
const std::string* privilege_str =
privilege.get_ptr<const std::string*>();
if (privilege_str == nullptr) {
LOG(WARNING) << "The value of the privilege isn't a string.";
LOG(WARNING) << "|value|=" << privilege.dump(2);
continue;
}
redfish_privileges.InsertPrivilege(*privilege_str);
}
// NOTE: there won't be an empty required privilege set ever
if (redfish_privileges.GetPrivileges().empty()) {
continue;
}
privileges_or_arr.push_back(redfish_privileges);
}
return privileges_or_arr;
}
std::vector<std::string> RedfishAuthorizer::GetTargetsArrFromJsonArr(
const nlohmann::json::array_t& targets_arr_json) const {
std::vector<std::string> targets;
for (const nlohmann::json& target : targets_arr_json) {
const std::string* target_str = target.get_ptr<const std::string*>();
if (target_str == nullptr) {
continue;
}
targets.push_back(*target_str);
}
return targets;
}
absl::flat_hash_map<ecclesia::Operation, std::vector<RedfishPrivileges>>
RedfishAuthorizer::GetOperationMapFromJson(
const nlohmann::json& override_operation_map) const {
absl::flat_hash_map<ecclesia::Operation, std::vector<RedfishPrivileges>>
override_operation_privileges_map;
for (Operation operation : all_operations) {
const nlohmann::json::array_t* override_operation_privilege_arr =
GetValueAsArray(override_operation_map, OperationToString(operation));
if (override_operation_privilege_arr == nullptr) {
continue;
}
override_operation_privileges_map[operation] =
GetPrivilegesArrFromJsonArr(*override_operation_privilege_arr);
}
return override_operation_privileges_map;
}
void RedfishAuthorizer::ReconstructPrivilegeRegistry(
const std::string& base_registry_path,
const nlohmann::json::array_t* oem_mappings,
const nlohmann::json* override_mappings,
const nlohmann::json& platform_config_json) {
std::ifstream registry_file(base_registry_path);
if (!registry_file.is_open()) {
LOG(WARNING) << "Base registry file at '" << base_registry_path
<< "' is missing.";
return;
}
std::string registry = {std::istreambuf_iterator<char>(registry_file),
std::istreambuf_iterator<char>()};
constexpr bool kAllowException = false;
nlohmann::json registry_json =
nlohmann::json::parse(registry, nullptr, kAllowException);
if (registry_json.is_discarded()) {
LOG(WARNING) << "Base registry file at '" << base_registry_path
<< "' is not a JSON document.";
return;
}
const nlohmann::json::array_t* mappings =
GetValueAsArray(registry_json, "Mappings");
const nlohmann::json::array_t* additional_subtree_mappings =
GetValueAsArray(registry_json, "AdditionalSubtreeMappings");
const nlohmann::json::array_t* platform_subtree_mappings =
GetValueAsArray(platform_config_json, "AdditionalSubtreeMappings");
ReconstructPrivilegeRegistry(mappings, oem_mappings, override_mappings,
additional_subtree_mappings,
platform_subtree_mappings);
}
void RedfishAuthorizer::ReconstructPrivilegeRegistry(
const nlohmann::json::array_t* registry_mappings,
const nlohmann::json::array_t* oem_mappings,
const nlohmann::json* override_mappings,
const nlohmann::json::array_t* additional_subtree_mappings,
const nlohmann::json::array_t* platform_subtree_mappings) {
if (registry_mappings == nullptr) {
return;
}
EntityPrivilegesMappings new_entity_privileges_mappings =
DefaultEntityPrivilegesMappings();
OverrideMappings new_override_mappings = DefaultOverrideMappings();
for (const nlohmann::json& mapping : *registry_mappings) {
const std::string* entity_str = GetValueAsString(mapping, "Entity");
const nlohmann::json* operation_map =
GetValueAsJson(mapping, "OperationMap");
if (operation_map == nullptr || entity_str == nullptr) {
continue;
}
ResourceEntity entity = StringToResourceEntity(*entity_str);
if (entity == ResourceEntity::kUndefined) {
continue;
}
for (Operation operation : all_operations) {
const nlohmann::json::array_t* operation_privilege_arr =
GetValueAsArray(*operation_map, OperationToString(operation));
if (operation_privilege_arr == nullptr) {
continue;
}
new_entity_privileges_mappings[static_cast<int>(entity)][static_cast<int>(
operation)] = GetPrivilegesArrFromJsonArr(*operation_privilege_arr);
}
// Apply overrides if applicable
ApplySubordinateOverridesToEntity(
GetValueAsArray(mapping, "SubordinateOverrides"), entity,
new_override_mappings);
ApplyResourceOverridesToEntity(
GetValueAsArray(mapping, "ResourceURIOverrides"), entity,
new_override_mappings);
}
if (oem_mappings != nullptr) {
ApplyOemMappings(*oem_mappings, new_entity_privileges_mappings);
}
if (override_mappings != nullptr) {
ApplyOverrideMappings(*override_mappings, new_override_mappings);
}
std::vector<SubtreePrivilegeMapping> additional_subtree_mappings_vector;
if (additional_subtree_mappings != nullptr) {
ParseAdditionalSubtreeMappings(additional_subtree_mappings_vector,
*additional_subtree_mappings);
}
if (platform_subtree_mappings != nullptr) {
ParseAdditionalSubtreeMappings(additional_subtree_mappings_vector,
*platform_subtree_mappings);
}
redfish_entity_trie_.ReplaceSubtreePrivilegeMappings(
std::move(additional_subtree_mappings_vector));
SetPrivileges(std::move(new_entity_privileges_mappings),
std::move(new_override_mappings));
}
void RedfishAuthorizer::ApplySubordinateOverrides(
const nlohmann::json::array_t* subordinate_overrides,
OverrideMappings& new_override_mappings) {
if (subordinate_overrides == nullptr) {
return;
}
for (const nlohmann::json& subordinate_override : *subordinate_overrides) {
const std::string* entity_str =
GetValueAsString(subordinate_override, "entity");
const nlohmann::json::array_t* targets_json =
GetValueAsArray(subordinate_override, "targets");
const nlohmann::json* override_operation_map =
GetValueAsJson(subordinate_override, "operation_map");
if (entity_str == nullptr || targets_json == nullptr ||
targets_json->empty() || override_operation_map == nullptr) {
LOG(WARNING) << "Override is setup incorrectly";
LOG(WARNING) << "|json|=" << subordinate_override.dump(2);
continue;
}
ResourceEntity entity = StringToResourceEntity(*entity_str);
if (entity == ResourceEntity::kUndefined) {
LOG(WARNING) << "|entity| is unsupported; |entity|=" << *entity_str;
continue;
}
std::vector<std::string> targets = GetTargetsArrFromJsonArr(*targets_json);
if (targets.empty()) {
continue;
}
new_override_mappings[static_cast<int>(entity)].push_back(
std::make_shared<SubordinateOverride>(
std::move(targets),
GetOperationMapFromJson(*override_operation_map),
redfish_entity_trie_));
}
}
void RedfishAuthorizer::ApplySubordinateOverridesToEntity(
const nlohmann::json::array_t* subordinate_overrides, ResourceEntity entity,
OverrideMappings& new_override_mappings) {
if (subordinate_overrides == nullptr) {
return;
}
for (const nlohmann::json& subordinate_override : *subordinate_overrides) {
const nlohmann::json::array_t* targets_json =
GetValueAsArray(subordinate_override, "Targets");
const nlohmann::json* override_operation_map =
GetValueAsJson(subordinate_override, "OperationMap");
if (targets_json == nullptr || override_operation_map == nullptr) {
continue;
}
std::vector<std::string> targets = GetTargetsArrFromJsonArr(*targets_json);
if (targets.empty()) {
continue;
}
new_override_mappings[static_cast<int>(entity)].push_back(
std::make_shared<SubordinateOverride>(
std::move(targets),
GetOperationMapFromJson(*override_operation_map),
redfish_entity_trie_));
}
}
void RedfishAuthorizer::ApplyResourceOverrides(
const nlohmann::json::array_t* resource_overrides,
OverrideMappings& new_override_mappings) {
if (resource_overrides == nullptr) {
return;
}
for (const nlohmann::json& resource_override : *resource_overrides) {
const std::string* entity_str =
GetValueAsString(resource_override, "entity");
const nlohmann::json::array_t* targets_json =
GetValueAsArray(resource_override, "targets");
const nlohmann::json* override_operation_map =
GetValueAsJson(resource_override, "operation_map");
if (entity_str == nullptr || targets_json == nullptr ||
targets_json->empty() || override_operation_map == nullptr) {
LOG(WARNING) << "Override is setup incorrectly";
LOG(WARNING) << "|json|=" << resource_override.dump(2);
continue;
}
ResourceEntity entity = StringToResourceEntity(*entity_str);
if (entity == ResourceEntity::kUndefined) {
LOG(WARNING) << "|entity| is unsupported; |entity|=" << *entity_str;
continue;
}
std::vector<std::string> targets = GetTargetsArrFromJsonArr(*targets_json);
if (targets.empty()) {
continue;
}
new_override_mappings[static_cast<int>(entity)].push_back(
std::make_shared<ResourceUriOverride>(
std::move(targets),
GetOperationMapFromJson(*override_operation_map)));
}
}
void RedfishAuthorizer::ApplyResourceOverridesToEntity(
const nlohmann::json::array_t* resource_overrides, ResourceEntity entity,
OverrideMappings& new_override_mappings) {
if (resource_overrides == nullptr) {
return;
}
for (const nlohmann::json& resource_override : *resource_overrides) {
const nlohmann::json::array_t* targets_json =
GetValueAsArray(resource_override, "Targets");
const nlohmann::json* override_operation_map =
GetValueAsJson(resource_override, "OperationMap");
if (targets_json == nullptr || override_operation_map == nullptr) {
continue;
}
std::vector<std::string> targets = GetTargetsArrFromJsonArr(*targets_json);
if (targets.empty()) {
continue;
}
new_override_mappings[static_cast<int>(entity)].push_back(
std::make_shared<ResourceUriOverride>(
std::move(targets),
GetOperationMapFromJson(*override_operation_map)));
}
}
void RedfishAuthorizer::ApplyOverrideMappings(
const nlohmann::json& override_mappings_json,
OverrideMappings& new_override_mappings) {
ApplySubordinateOverrides(
GetValueAsArray(override_mappings_json, "subordinate_overrides"),
new_override_mappings);
ApplyResourceOverrides(
GetValueAsArray(override_mappings_json, "resource_overrides"),
new_override_mappings);
}
void RedfishAuthorizer::ApplyOemMappings(
const nlohmann::json::array_t& oem_mappings,
EntityPrivilegesMappings& entity_privileges_mappings) {
for (const nlohmann::json& oem_mapping : oem_mappings) {
const std::string* oem_privilege =
GetValueAsString(oem_mapping, "oem_privilege");
if (oem_privilege == nullptr) {
continue;
}
RedfishPrivileges new_privileges({*oem_privilege});
const nlohmann::json::array_t* resource_operations =
GetValueAsArray(oem_mapping, "resource_operations");
if (resource_operations == nullptr) {
continue;
}
for (const nlohmann::json& resource_operation : *resource_operations) {
const std::string* resource_entity_str =
GetValueAsString(resource_operation, "resource_entity");
if (resource_entity_str == nullptr) {
continue;
}
ResourceEntity entity = StringToResourceEntity(*resource_entity_str);
if (entity == ResourceEntity::kUndefined) {
LOG(WARNING) << "|resource_entity| is unsupported; |resource_entity|="
<< *resource_entity_str;
continue;
}
const nlohmann::json::array_t* operations =
GetValueAsArray(resource_operation, "operations");
if (operations == nullptr) {
continue;
}
for (const nlohmann::json& operation_json : *operations) {
const std::string* operation_str =
operation_json.get_ptr<const std::string*>();
if (operation_str == nullptr) {
LOG(WARNING) << "The value of the operation isn't a string.";
LOG(WARNING) << "|value|=" << operation_json;
continue;
}
Operation operation = StringToOperation(*operation_str);
if (operation == Operation::kUndefined) {
LOG(WARNING) << "|operation| is unsupported; |operation|="
<< *operation_str;
continue;
}
AppendPrivilege(new_privileges, entity, operation,
entity_privileges_mappings);
}
}
}
}
void RedfishAuthorizer::ParseAdditionalSubtreeMappings(
std::vector<SubtreePrivilegeMapping>& additional_subtree_mappings,
const nlohmann::json& additional_subtree_mappings_json) {
for (const nlohmann::json& subtree_mapping :
additional_subtree_mappings_json) {
const std::string* subtree_prefix =
GetValueAsString(subtree_mapping, "SubtreePrefix");
const nlohmann::json* operation_map_json =
GetValueAsJson(subtree_mapping, "OperationMap");
if (subtree_prefix == nullptr || operation_map_json == nullptr) {
continue;
}
for (const auto& [operation, privileges] :
GetOperationMapFromJson(*operation_map_json)) {
for (const auto& privilege : privileges) {
additional_subtree_mappings.push_back(
SubtreePrivilegeMapping{.url = *subtree_prefix,
.operation = operation,
.privileges = privilege});
}
}
}
}
void RedfishAuthorizer::AppendPrivilege(
const RedfishPrivileges& privileges, ResourceEntity resource_entity,
Operation operation, EntityPrivilegesMappings& entity_privileges_mappings) {
entity_privileges_mappings[static_cast<int>(resource_entity)]
[static_cast<int>(operation)]
.push_back(privileges);
}
std::vector<RedfishPrivileges> RedfishAuthorizer::GetRequiredPrivileges(
ResourceEntity resource_entity, Operation operation) const {
absl::MutexLock lock(&mutex_);
return entity_privileges_mappings_[static_cast<int>(resource_entity)]
[static_cast<int>(operation)];
}
std::vector<std::shared_ptr<Override>> RedfishAuthorizer::GetOverrides(
ResourceEntity resource_entity) const {
absl::MutexLock lock(&mutex_);
return override_mappings_[static_cast<int>(resource_entity)];
}
absl::Status RedfishAuthorizer::RecordNewSubscription(
const PeerSpiffeIdentity& peer) {
return subscription_tracker_.RecordNewSubscription(peer);
}
absl::Status RedfishAuthorizer::RecordNewUnsubscription(
const PeerSpiffeIdentity& peer) {
return subscription_tracker_.RecordNewUnsubscription(peer);
}
bool RedfishAuthorizer::IsPeerAuthorized(
ResourceEntity entity, Operation operation,
const RedfishPrivileges& peer_privileges) const {
if (entity == ResourceEntity::kUndefined ||
operation == Operation::kUndefined) {
return false;
}
std::vector<RedfishPrivileges> required_privilege_sets =
GetRequiredPrivileges(entity, operation);
for (const RedfishPrivileges& privileges : required_privilege_sets) {
// NOTE: there won't be an empty required privilege set ever; otherwise,
// we skip this required privilege set.
if (privileges.GetPrivileges().empty()) {
LOG(WARNING)
<< "Precondition failed: required privilege set will never be "
"empty; check the base privilege registry for "
<< ResourceEntityToString(entity) << " and operation "
<< OperationToString(operation);
continue;
}
if (peer_privileges.IsSupersetOf(privileges)) {
return true;
}
}
return false;
}
bool RedfishAuthorizer::IsPeerAuthorized(
std::string_view uri, Operation operation,
const RedfishPrivileges& peer_privileges) const {
ResourceEntity entity = GetEntityTypeFromRedfishUri(uri);
if (entity == ResourceEntity::kUndefined) {
LOG(WARNING) << "Can't map |uri| to Redfish resource; |uri|=" << uri;
return false;
}
if (operation == Operation::kUndefined) {
LOG(WARNING) << "|operation| is undefined";
return false;
}
// Check Overrides
for (const std::shared_ptr<Override>& override : GetOverrides(entity)) {
// If the override is applicable, then peer must be authorized through
// override
if (override->IsApplicable(uri, operation)) {
return override->IsPeerAuthorized(uri, operation, peer_privileges);
}
}
// Check Additional Subtree Mappings
for (const auto& privileges :
redfish_entity_trie_.GetAdditionalSubtreePrivileges(uri, operation)) {
// NOTE: there won't be an empty required privilege set ever; otherwise,
// we skip this required privilege set.
if (privileges.GetPrivileges().empty()) {
LOG(WARNING)
<< "Precondition failed: required privilege set will never be "
"empty; check the additional subtree mappings for "
<< uri << " and operation " << OperationToString(operation);
continue;
}
if (peer_privileges.IsSupersetOf(privileges)) {
return true;
}
}
return IsPeerAuthorized(entity, operation, peer_privileges);
}
nlohmann::json::array_t RedfishAuthorizer::RedfishPrivilegeRegistryMappings()
const {
nlohmann::json::array_t privilege_registry_mappings;
for (size_t i = 0; i < static_cast<size_t>(ResourceEntity::kUndefined); ++i) {
nlohmann::json::object_t entity_privilege_mappings;
ResourceEntity entity = static_cast<ResourceEntity>(i);
entity_privilege_mappings["Entity"] = ResourceEntityToString(entity);
// Get OperationMap
entity_privilege_mappings["OperationMap"] = nlohmann::json::object_t();
for (size_t j = 0; j < static_cast<size_t>(Operation::kUndefined); ++j) {
nlohmann::json::array_t operation_privileges;
Operation operation = static_cast<Operation>(j);
std::vector<RedfishPrivileges> privilege_set =
GetRequiredPrivileges(entity, operation);
for (const RedfishPrivileges& privileges : privilege_set) {
operation_privileges.push_back(privileges.ToJson());
}
entity_privilege_mappings["OperationMap"][OperationToString(operation)] =
operation_privileges;
}
// Get Overrides
std::vector<std::shared_ptr<Override>> overrides = GetOverrides(entity);
nlohmann::json::array_t subordinate_overrides_json =
nlohmann::json::array_t();
nlohmann::json::array_t resource_overrides_json = nlohmann::json::array_t();
for (const std::shared_ptr<Override>& override : overrides) {
if (override->GetOverrideType() == Override::Type::kSubordinateOverride) {
subordinate_overrides_json.push_back(override->ToJson());
continue;
}
resource_overrides_json.push_back(override->ToJson());
}
if (!subordinate_overrides_json.empty()) {
entity_privilege_mappings["SubordinateOverrides"] =
subordinate_overrides_json;
}
if (!resource_overrides_json.empty()) {
entity_privilege_mappings["ResourceURIOverrides"] =
resource_overrides_json;
}
privilege_registry_mappings.push_back(entity_privilege_mappings);
}
return privilege_registry_mappings;
}
nlohmann::json::object_t RedfishAuthorizer::GetRedfishPrivilegeRegistry()
const {
nlohmann::json::object_t privilege_registry;
privilege_registry["@odata.id"] = "/redfish/v1/AccountService/PrivilegeMap";
privilege_registry["@odata.type"] =
"#PrivilegeRegistry.v1_1_4.PrivilegeRegistry";
privilege_registry["Id"] = "Redfish_1.3.1_PrivilegeRegistry";
privilege_registry["Name"] = "Privilege Mapping array collection";
privilege_registry["PrivilegesUsed"] =
authz_configuration_.GetDefaultRedfishPrivileges();
privilege_registry["OEMPrivilegesUsed"] =
authz_configuration_.GetOemPrivileges();
privilege_registry["Mappings"] = RedfishPrivilegeRegistryMappings();
return privilege_registry;
}
} // namespace milotic::authz