/* // Copyright (c) 2018 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #pragma once #include #include #include "crow.h" #include #include namespace redfish { enum class PrivilegeType { BASE, OEM }; /** @brief Max number of privileges per type */ constexpr const size_t MAX_PRIVILEGE_COUNT = 32; using privilegeBitset = std::bitset; /** @brief Number of mappings must be <= MAX_PRIVILEGE_COUNT */ static const std::vector privilegeNames{ "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", "ConfigureUsers"}; /** @brief Number of mappings must be <= MAX_PRIVILEGE_COUNT */ static const std::vector oemPrivilegeNames{}; /** * @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. * * A bit is set if the privilege is required (entity domain) or granted * (user domain) and false otherwise. * */ class Privileges { public: /** * @brief Constructs object without any privileges active * */ Privileges() = default; /** * @brief Constructs object with given privileges active * * @param[in] privilegeList List of privileges to be activated * */ Privileges(std::initializer_list privilegeList) { for (const char* privilege : privilegeList) { if (!setSinglePrivilege(privilege)) { CROW_LOG_CRITICAL << "Unable to set privilege " << privilege << "in constructor"; } } } /** * @brief Sets given privilege in the bitset * * @param[in] privilege Privilege to be set * * @return None * */ bool setSinglePrivilege(const char* privilege) { int32_t index = getBitsetIndexForPrivilege(privilege, PrivilegeType::BASE); if (index >= 0) { basePrivilegeBitset.set(index); return true; } index = getBitsetIndexForPrivilege(privilege, PrivilegeType::OEM); if (index >= 0) { oemPrivilegeBitset.set(index); return true; } return false; } /** * @brief Sets given privilege in the bitset * * @param[in] privilege Privilege to be set * * @return None * */ bool setSinglePrivilege(const std::string& privilege) { return setSinglePrivilege(privilege.c_str()); } /** * @brief Retrieves names of all active privileges for a given type * * @param[in] type Base or OEM * * @return Vector of active privileges. Pointers are valid until * the privilege structure is modified * */ std::vector getActivePrivilegeNames( const PrivilegeType type) const { std::vector activePrivileges; if (type == PrivilegeType::BASE) { for (std::size_t index = 0; index < privilegeNames.size(); index++) { if (basePrivilegeBitset.test(index)) { activePrivileges.emplace_back(&privilegeNames[index]); } } } else { for (std::size_t index = 0; index < oemPrivilegeNames.size(); index++) { { if (oemPrivilegeBitset.test(index)) { activePrivileges.emplace_back(&oemPrivilegeNames[index]); } } } } return activePrivileges; } /** * @brief Determines if this Privilege set is a superset of the given * privilege set * * @param[in] privilege Privilege to be checked * * @return None * */ bool isSupersetOf(const Privileges& p) const { bool has_base = (basePrivilegeBitset & p.basePrivilegeBitset) == p.basePrivilegeBitset; bool has_oem = (oemPrivilegeBitset & p.oemPrivilegeBitset) == p.oemPrivilegeBitset; return has_base & has_oem; } private: int32_t getBitsetIndexForPrivilege(const char* privilege, const PrivilegeType type) const { if (type == PrivilegeType::BASE) { for (std::size_t index = 0; index < privilegeNames.size(); index++) { if (privilege == privilegeNames[index]) { return index; } } } else { for (std::size_t index = 0; index < oemPrivilegeNames.size(); index++) { if (privilege == oemPrivilegeNames[index]) { return index; } } } return -1; } privilegeBitset basePrivilegeBitset = 0; privilegeBitset oemPrivilegeBitset = 0; }; using OperationMap = boost::container::flat_map>; /** * @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 * */ inline bool isMethodAllowedWithPrivileges(const crow::HTTPMethod method, const OperationMap& operationMap, const Privileges& userPrivileges) { const auto& it = operationMap.find(method); if (it == operationMap.end()) { return false; } // If there are no privileges assigned, assume no privileges required if (it->second.empty()) { return true; } for (auto& requiredPrivileges : it->second) { if (userPrivileges.isSupersetOf(requiredPrivileges)) { return true; } } return false; } /** * @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 * */ inline bool isMethodAllowedForUser(const crow::HTTPMethod method, const OperationMap& operationMap, const std::string& user) { // TODO: load user privileges from configuration as soon as its available // now we are granting all privileges to everyone. Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf", "ConfigureUsers", "ConfigureComponents"}; return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges); } } // namespace redfish