Redfish privileges
Redfish privilege authorization subsystem controlled by the
privilege_registy.json configuration file.
PropertyOverrides, SubordinateOverrides and ResourceURIOverrides
are not yet implemented.
Change-Id: I4d5670d557f4da172460ada3512e015830dab667
Signed-off-by: Borawski.Lukasz <lukasz.borawski@intel.com>
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/redfish-core/include/privileges.hpp b/redfish-core/include/privileges.hpp
index 290c0eb..2441104 100644
--- a/redfish-core/include/privileges.hpp
+++ b/redfish-core/include/privileges.hpp
@@ -15,51 +15,228 @@
*/
#pragma once
+#include <bitset>
+#include <cstdint>
+#include "crow.h"
+#include <boost/container/flat_map.hpp>
+#include <boost/optional.hpp>
+
namespace redfish {
+class PrivilegeProvider;
+
+enum class PrivilegeType { BASE, OEM };
+
+/** @brief Max number of privileges per type */
+constexpr const size_t MAX_PRIVILEGE_COUNT = 32;
+using privilegeBitset = std::bitset<MAX_PRIVILEGE_COUNT>;
+
/**
- * @brief Class used to store privileges for a given user.
+ * @brief Redfish privileges
+ *
+ * Entity privileges and user privileges are represented by this class.
+ *
+ * Each incoming connection requires a comparison between privileges held
+ * by the user issuing a request and the target entity's privileges.
+ *
+ * To ensure best runtime performance of this comparison, privileges
+ * are represented as bitsets. Each bit in the bitset corresponds to a
+ * unique privilege name.
+ *
+ * Privilege names are read from the privilege_registry.json file and
+ * stored in flat maps.
+ *
+ * A bit is set if the privilege is required (entity domain) or granted
+ * (user domain) and false otherwise.
+ *
+ * Bitset index to privilege name mapping depends on the order in which
+ * privileges are defined in PrivilegesUsed and OEMPrivilegesUsed arrays
+ * in the privilege_registry.json.
*/
-class UserPrivileges {
- // TODO: Temporary stub, implementation will come with next patch-sets
+class Privileges {
+ public:
+ /**
+ * @brief Retrieves the base privileges bitset
+ *
+ * @return Bitset representation of base Redfish privileges
+ */
+ privilegeBitset getBasePrivilegeBitset() const { return basePrivilegeBitset; }
+
+ /**
+ * @brief Retrieves the OEM privileges bitset
+ *
+ * @return Bitset representation of OEM Redfish privileges
+ */
+ privilegeBitset getOEMPrivilegeBitset() const { return oemPrivilegeBitset; }
+
+ /**
+ * @brief Sets given privilege in the bitset
+ *
+ * @param[in] privilege Privilege to be set
+ *
+ * @return None
+ */
+ void setSinglePrivilege(const std::string& privilege) {
+ auto index = getBitsetIndexForPrivilege(privilege, PrivilegeType::BASE);
+ if (index) {
+ basePrivilegeBitset.set(*index);
+ return;
+ }
+
+ index = getBitsetIndexForPrivilege(privilege, PrivilegeType::OEM);
+ if (index) {
+ oemPrivilegeBitset.set(*index);
+ }
+ }
+
+ /**
+ * @brief Retrieves names of all active privileges for a given type
+ *
+ * @param[in] type Base or OEM
+ *
+ * @return Vector of active privileges
+ */
+ std::vector<std::string> getActivePrivilegeNames(
+ const PrivilegeType type) const {
+ std::vector<std::string> activePrivileges;
+
+ if (type == PrivilegeType::BASE) {
+ for (const auto& pair : basePrivNameToIndexMap) {
+ if (basePrivilegeBitset.test(pair.second)) {
+ activePrivileges.emplace_back(pair.first);
+ }
+ }
+ } else {
+ for (const auto& pair : oemPrivNameToIndexMap) {
+ if (oemPrivilegeBitset.test(pair.second)) {
+ activePrivileges.emplace_back(pair.first);
+ }
+ }
+ }
+
+ return activePrivileges;
+ }
+
private:
- uint32_t redfishPrivileges;
- uint32_t oemPrivileges;
+ boost::optional<size_t> getBitsetIndexForPrivilege(
+ const std::string& privilege, const PrivilegeType type) const {
+ if (type == PrivilegeType::BASE) {
+ const auto pair = basePrivNameToIndexMap.find(privilege);
+ if (pair != basePrivNameToIndexMap.end()) {
+ return pair->second;
+ }
+ } else {
+ const auto pair = oemPrivNameToIndexMap.find(privilege);
+ if (pair != oemPrivNameToIndexMap.end()) {
+ return pair->second;
+ }
+ }
+
+ return boost::none;
+ }
+
+ privilegeBitset basePrivilegeBitset;
+ privilegeBitset oemPrivilegeBitset;
+
+ static boost::container::flat_map<std::string, size_t> basePrivNameToIndexMap;
+ static boost::container::flat_map<std::string, size_t> oemPrivNameToIndexMap;
+
+ friend class PrivilegeProvider;
};
/**
- * @brief Class used to store privileges for a given Redfish entity.
+ * @brief Class used to store privileges for Redfish entities
*/
class EntityPrivileges {
- // TODO: Temporary stub, implementation will come with next patch-sets
public:
- bool isMethodAllowed(const crow::HTTPMethod& method,
- const std::string& username) const {
- return true;
+ /**
+ * @brief Checks if a user is allowed to call an HTTP method
+ *
+ * @param[in] method HTTP method
+ * @param[in] user Username
+ *
+ * @return True if method allowed, false otherwise
+ */
+ bool isMethodAllowedForUser(const crow::HTTPMethod method,
+ const std::string& user) const;
+
+ /**
+ * @brief Checks if given privileges allow to call an HTTP method
+ *
+ * @param[in] method HTTP method
+ * @param[in] user Privileges
+ *
+ * @return True if method allowed, false otherwise
+ */
+ bool isMethodAllowedWithPrivileges(const crow::HTTPMethod method,
+ const Privileges& userPrivileges) const;
+
+ /**
+ * @brief Sets required privileges for a method on a given entity
+ *
+ * @param[in] method HTTP method
+ * @param[in] privileges Required privileges
+ *
+ * @return None
+ */
+ void addPrivilegesRequiredByMethod(const crow::HTTPMethod method,
+ const Privileges& privileges) {
+ methodToPrivilegeMap[method].push_back(privileges);
}
+
+ private:
+ bool verifyPrivileges(const privilegeBitset userPrivilegeBitset,
+ const privilegeBitset requiredPrivilegeBitset) const;
+
+ boost::container::flat_map<crow::HTTPMethod, std::vector<Privileges>>
+ methodToPrivilegeMap;
};
/**
* @brief Class used to:
- * - read the PrivilegeRegistry file,
- * - provide EntityPrivileges objects to callers.
+ * - read the privilege_registry.json file
+ * - provide EntityPrivileges objects to callers
*
- * To save runtime memory object of this class should
- * exist only for the time required to install all Nodes.
+ * To save runtime memory, object of this class should
+ * exist only for the time required to install all Nodes
*/
class PrivilegeProvider {
- // TODO: Temporary stub, implementation will come with next patch-sets
public:
- PrivilegeProvider() {
- // load privilege_registry.json to memory
+ PrivilegeProvider(const std::string& privilegeRegistryPath) {
+ // TODO: read this path from the configuration once its available
+ std::ifstream privilegeRegistryFile{privilegeRegistryPath};
+
+ if (privilegeRegistryFile.is_open()) {
+ if (!loadPrivilegesFromFile(privilegeRegistryFile)) {
+ privilegeRegistryJson.clear();
+ CROW_LOG_ERROR << "Couldn't parse privilege_registry.json";
+ }
+ } else {
+ CROW_LOG_ERROR << "Couldn't open privilege_registry.json";
+ }
}
- EntityPrivileges getPrivileges(const std::string& entity_url,
- const std::string& entity_type) const {
- // return an entity privilege object based on the privilege_registry.json,
- // currently returning default constructed object
- return EntityPrivileges();
- }
+ /**
+ * @brief Gets required privileges for a certain entity type
+ *
+ * @param[in] entityUrl Entity url
+ * @param[in] entityType Entity type
+ *
+ * @return EntityPrivilege object
+ */
+ EntityPrivileges getPrivilegesRequiredByEntity(
+ const std::string& entityUrl, const std::string& entityType) const;
+
+ private:
+ bool loadPrivilegesFromFile(std::ifstream& privilegeRegistryFile);
+ bool privilegeRegistryHasRequiredFields() const;
+ bool parseOperationMap(const nlohmann::json& operationMap,
+ EntityPrivileges& entityPrivileges) const;
+ bool fillPrivilegeMap(const nlohmann::json& privilegesUsed,
+ boost::container::flat_map<std::string, size_t>&
+ privilegeToIndexMap) const;
+
+ nlohmann::json privilegeRegistryJson;
};
} // namespace redfish