blob: 5a728e24690f029e3dae4eefed37ff69d0bf4a7b [file] [log] [blame]
#ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_AUTHZ_REDFISH_ENTITY_TRIE_H_
#define THIRD_PARTY_MILOTIC_EXTERNAL_CC_AUTHZ_REDFISH_ENTITY_TRIE_H_
#include <cstddef>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/strings/substitute.h"
#include "absl/synchronization/mutex.h"
#include "authorizer_enums.h"
#include "redfish_entity_trie_node.h"
#include "redfish_privileges.h"
#include "redfish_subtree_privileges_trie_node.h"
namespace milotic::authz {
/*
Redfish Entity Trie intended to be used as a method to obtain an entity type
when given a redfish path regardless of if wildcards are inputted.
Sample usage:
"I know that /redfish/v1/Chassis/{ChassisId} has an entity type Chassis from the
schema". This can be entered using
trie.InsertUri("/redfish/v1/Chassis/{ChassisId}", "Chassis");
"I want to figure out the entity type for /redfish/v1/Chassis/test"
This can be found by
trie.GetEntityType("/redfish/v1/Chassis/test") -> "Chassis"
Another usage of this trie is to obtain all the privileges that is able to
access this URI via subtree mapping.
"I want privilege 'ChassisXOwner' to be able to access every URI with prefix
'/redfish/v1/Chassis/ChassisX'"
This can be entered using
trie.SetSubtreePrivilegeMappings({{"/redfish/v1/Chassis/ChassisX",
"ChassisXOwner"}});
"I want to see what additional OEM privileges can access
'/redfish/v1/Chassis/ChassisX/Sensors/sensorY"
This can be found by
trie.GetAdditionalSubtreePrivileges("/redfish/v1/Chassis/ChassisX/Sensors/sensorY");
ReplaceSubtreeMappings is done instead of exposing AddSubtreeMapping to public
and allowing modification after initialization to subtree privileges due to
duplication issues that can occur. If we had exposed AddSubtreeMapping to
public, then there are orders in which duplications can occur in subtrees that
make it hard for deduplication to occur.
AddPrivilegeForSubtree("/redfish/v1", "SubtreePrivileges1");
AddPrivilegeForSubtree("/redfish/v1/Chassis", "SubtreePrivileges1");
When performing the queries above, the second AddPrivilege doesn't mean
anything, so we ignore the second command as we see a duplicate when traversing
down the trie.
However if we perform
AddPrivilegeForSubtree("/redfish/v1/Chassis", "SubtreePrivileges1");
AddPrivilegeForSubtree("/redfish/v1", "SubtreePrivileges1");
Then, it is impossible to tell without iterating through the rest of the trie
that a duplication exists. The current workaround is to sort it first so we can
always find the duplicates, but an alternative that was deemed to complex for
the first iteration is lazy propagation.
NOTE: This class is only partially thread safe. Please check the method's
documentation to see if that method is thread safe or not
*/
// Struct to hold information regarding individual subtree privilege mapping
struct SubtreePrivilegeMapping {
std::string url;
ecclesia::Operation operation;
RedfishPrivileges privileges;
bool operator<(const SubtreePrivilegeMapping& other) const {
return url < other.url;
}
std::string ToString() const {
return absl::Substitute("URL: $0, Operation: $1 has privileges: $2", url,
::ecclesia::OperationToString(operation),
privileges.GetDebugString());
}
};
class RedfishEntityTrie {
public:
RedfishEntityTrie();
// Inserts a URI into the entity trie. NOT THREAD SAFE.
void InsertUri(std::string_view uri, ecclesia::ResourceEntity entity_tag,
std::size_t node_index_in_pattern_array);
// Supports normal and fragmented URIs.
// THREAD SAFE IF InsertUri IS NOT CALLED IN PARALLEL
// TODO (edwarddl): Support ignoring query parameters
ecclesia::ResourceEntity GetEntityType(std::string_view uri) const;
// THREAD SAFE after all InsertUri is done.
// Returns the index of the node in the pattern array. If the URI is not
// found, returns std::numeric_limits<std::size_t>::max()
std::size_t GetNodeIndexInPatternArray(std::string_view uri) const;
// Replace subtree mappings for trie. This will clear the current mappings and
// replace them with what is passed into the method. THREAD SAFE
// TODO: Consider removing this if we ever decide to implement lazy
// propagation
void ReplaceSubtreePrivilegeMappings(
std::vector<SubtreePrivilegeMapping>&& subtree_privilege_mappings);
// Get privileges that can access to uri via additional subtree mappings
// THREAD SAFE
absl::flat_hash_set<RedfishPrivileges> GetAdditionalSubtreePrivileges(
std::string_view uri, const ecclesia::Operation& operation) const;
private:
const internal::RedfishEntityTrieNode* GetTrieNode(
std::string_view uri) const;
// We only support setting and removing subtree mappings. We don't support
// individual additions.
absl::Status AddSubtreeMapping(
SubtreePrivilegeMapping&& subtree_privilege_mapping)
ABSL_SHARED_LOCKS_REQUIRED(subtree_privileges_mutex_);
mutable absl::Mutex subtree_privileges_mutex_;
std::unique_ptr<internal::RedfishEntityTrieNode> entity_trie_root_;
std::unique_ptr<internal::RedfishSubtreePrivilegesTrieNode>
subtree_privileges_trie_root_ ABSL_GUARDED_BY(subtree_privileges_mutex_);
};
} // namespace milotic::authz
#endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_AUTHZ_REDFISH_ENTITY_TRIE_H_