| #ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_AUTHZ_REDFISH_AUTHORIZER_H_ |
| #define THIRD_PARTY_MILOTIC_EXTERNAL_CC_AUTHZ_REDFISH_AUTHORIZER_H_ |
| |
| #include <array> |
| #include <cstddef> |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <string_view> |
| #include <unordered_set> |
| #include <vector> |
| |
| #include "absl/base/thread_annotations.h" |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/status/status.h" |
| #include "absl/synchronization/mutex.h" |
| #include "authorizer_enums.h" |
| #include "nlohmann/json.hpp" |
| #include "config_parser.h" |
| #include "override.h" |
| #include "redfish_entity_trie.h" |
| #include "redfish_privileges.h" |
| #include "subscription_tracker.h" |
| |
| namespace milotic::authz { |
| |
| using ::ecclesia::Operation; |
| |
| constexpr std::array<Operation, static_cast<int>(Operation::kUndefined)> |
| all_operations = { |
| Operation::kGet, Operation::kHead, Operation::kPatch, |
| Operation::kPut, Operation::kDelete, Operation::kPost, |
| }; |
| |
| struct AuthorizerOptions { |
| // The configuration of the authorizer; the schema is defined by |
| // |third_party/milotic/external/auth_config/schema|. |
| std::string configuration_path; |
| // The platform specific configuration of the authorizer; the schema is |
| // defined by |
| // |third_party/milotic/external/auth_config/platform_config_schema|. |
| std::string platform_configuration_path; |
| // The folder where base privileges are stored |
| std::string base_privileges_folder; |
| // The path to the non-standard pattern to resource configuration. |
| // The use case of this configuration is rare. |
| // Please contact the 3M team (nanzhou@, edwarddl@) for consultation. |
| std::string pattern_entity_overrides_path; |
| |
| // The absolute path where Offline Node Entities file is. (Can be left empty) |
| // This should be the file installed on the node during RRI |
| // For more information see go/node-entities-api |
| std::string offline_node_entities_path; |
| |
| // The path where the Google Machine Identity is. If left empty, machines are |
| // automatically assumed to not be in HWOPS. For more information see |
| // go/google-machine-identity-token |
| std::string google_machine_identity_path; |
| |
| // Set to true to maximize debug logs. |
| bool verbose = true; |
| }; |
| |
| // An implementation of the Redfish authorization model. It supports runtime |
| // configuration reloading, OEM privileges, OEM roles, and privilege overrides. |
| // The class is thread-safe. |
| class RedfishAuthorizer { |
| public: |
| // EntityTypeIndex -> List of RedfishPrivileges for that entity type |
| using EntityPrivilegesMappings = |
| std::vector<std::vector<std::vector<RedfishPrivileges>>>; |
| // EntityTypeIndex -> List of Overrides for that entity type |
| using OverrideMappings = std::vector<std::vector<std::shared_ptr<Override>>>; |
| |
| RedfishAuthorizer(); |
| explicit RedfishAuthorizer(const AuthorizerOptions& options); |
| RedfishAuthorizer(const nlohmann::json& registry_json, |
| const nlohmann::json& configuration_json); |
| |
| virtual ~RedfishAuthorizer() = default; |
| |
| // Parse the Auth Config file into json |
| nlohmann::json ParseAuthConfig() const; |
| |
| // Find the path to the Base Privilege Registry |
| std::string FindBasePrivilegeRegistryPath( |
| const nlohmann::json& configuration_json) const; |
| |
| void ParseSubscriptionLimit(const nlohmann::json& configuration_json); |
| |
| // Reloads the authorization configuration. |
| void ReloadConfiguration(); |
| |
| // Reloads the authorization configuration from the given path. |
| // Useful when you want to reload the configuration from a different path. |
| void ReloadConfiguration(const std::string& configuration_path); |
| |
| void SetBasePrivilegesFolder(std::string_view base_privileges_folder); |
| |
| // Returns the peer's redfish role. Returns an empty string if the peer is not |
| // authorized. |
| std::string GetPeerRedfishRole(const PeerSpiffeIdentity& peer) const; |
| |
| // Returns the sample rate limit for the peer role. |
| uint64_t GetSampleRateLimit(const std::string& peer_role) const; |
| |
| std::unordered_set<std::string> GetPeerRedfishPrivileges( |
| const PeerSpiffeIdentity& peer) const { |
| return authz_configuration_.GetPeerRedfishPrivileges(peer); |
| } |
| std::unordered_set<std::string> GetRedfishPrivileges( |
| const std::string& role) const { |
| return authz_configuration_.GetRedfishPrivileges(role); |
| } |
| void LoadRedfishEntityTrie(); |
| ecclesia::ResourceEntity GetEntityTypeFromRedfishUri( |
| std::string_view uri) const; |
| |
| // This method doesn't support any override. |
| virtual bool IsPeerAuthorized(ecclesia::ResourceEntity resource_entity, |
| ecclesia::Operation operation, |
| const RedfishPrivileges& peer_privileges) const; |
| |
| // These methods support Resource and Subordinate Overrides but not Property |
| // Overrides |
| virtual bool IsPeerAuthorized(std::string_view uri, |
| ecclesia::Operation operation, |
| const RedfishPrivileges& peer_privileges) const; |
| |
| // Get the Redfish Privilege Registry for current authorizer |
| // This output follows the DMTF Privilege Registry schema |
| // http://redfish.dmtf.org/schemas/v1/PrivilegeRegistry.v1_1_4.json#/definitions/PrivilegeRegistry |
| nlohmann::json::object_t GetRedfishPrivilegeRegistry() const; |
| |
| // Records a new subscription and returns OK only if this subscription is |
| // within the limit. |
| absl::Status RecordNewSubscription(const PeerSpiffeIdentity& peer); |
| // Records a new unsubscription of the given client. |
| absl::Status RecordNewUnsubscription(const PeerSpiffeIdentity& peer); |
| int64_t GetSubscriptionLimit(const PeerSpiffeIdentity& peer) { |
| return subscription_tracker_.GetSubscriptionLimit(peer); |
| } |
| |
| std::size_t GetNodeIndexInPatternArray(std::string_view uri) const { |
| return redfish_entity_trie_.GetNodeIndexInPatternArray(uri); |
| } |
| |
| // TODO(nanzhou): add property override support. |
| protected: |
| absl::flat_hash_map<ecclesia::Operation, std::vector<RedfishPrivileges>> |
| GetOperationMapFromJson(const nlohmann::json& override_operation_map) const; |
| std::vector<RedfishPrivileges> GetPrivilegesArrFromJsonArr( |
| const nlohmann::json::array_t& operation_privilege_arr_json) const; |
| std::vector<std::string> GetTargetsArrFromJsonArr( |
| const nlohmann::json::array_t& targets_arr_json) const; |
| void 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); |
| void 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); |
| void ApplyOemMappings(const nlohmann::json::array_t& oem_mappings, |
| EntityPrivilegesMappings& entity_privileges_mappings); |
| void ApplySubordinateOverrides( |
| const nlohmann::json::array_t* subordinate_overrides, |
| OverrideMappings& new_override_mappings); |
| void ApplySubordinateOverridesToEntity( |
| const nlohmann::json::array_t* subordinate_overrides, |
| ecclesia::ResourceEntity entity, OverrideMappings& new_override_mappings); |
| void ApplyResourceOverrides(const nlohmann::json::array_t* resource_overrides, |
| OverrideMappings& new_override_mappings); |
| void ApplyResourceOverridesToEntity( |
| const nlohmann::json::array_t* resource_overrides, |
| ecclesia::ResourceEntity entity, OverrideMappings& new_override_mappings); |
| void ApplyOverrideMappings(const nlohmann::json& override_mappings_json, |
| OverrideMappings& new_override_mappings); |
| void ParseAdditionalSubtreeMappings( |
| std::vector<SubtreePrivilegeMapping>& additional_subtree_mappings, |
| const nlohmann::json& additional_subtree_mappings_json); |
| void ResetEntityPrivilegesMappings(); |
| void SetPrivileges(EntityPrivilegesMappings&& new_entity_privileges_mappings, |
| OverrideMappings&& new_override_mappings); |
| void AppendPrivilege(const RedfishPrivileges& privileges, |
| ecclesia::ResourceEntity resource_entity, |
| ecclesia::Operation operation, |
| EntityPrivilegesMappings& entity_privileges_mappings); |
| virtual std::vector<RedfishPrivileges> GetRequiredPrivileges( |
| ecclesia::ResourceEntity resource_entity, |
| ecclesia::Operation operation) const; |
| virtual std::vector<std::shared_ptr<Override>> GetOverrides( |
| ecclesia::ResourceEntity resource_entity) const; |
| static EntityPrivilegesMappings DefaultEntityPrivilegesMappings(); |
| static OverrideMappings DefaultOverrideMappings(); |
| nlohmann::json::array_t RedfishPrivilegeRegistryMappings() const; |
| |
| AuthorizerOptions options_; |
| mutable absl::Mutex mutex_; |
| // redfish_entity_trie_ is thread-safe for subtree mapping modification. |
| // InsertUri is NOT THREAD SAFE, but it is never called after initialization. |
| RedfishEntityTrie redfish_entity_trie_; |
| |
| AuthzConfiguration authz_configuration_; |
| |
| EntityPrivilegesMappings entity_privileges_mappings_ ABSL_GUARDED_BY(mutex_); |
| OverrideMappings override_mappings_ ABSL_GUARDED_BY(mutex_); |
| |
| SubscriptionTracker subscription_tracker_; |
| }; |
| |
| } // namespace milotic::authz |
| |
| #endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_AUTHZ_REDFISH_AUTHORIZER_H_ |