/* // 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 #include #include #include #include namespace redfish { enum class PrivilegeType { BASE, OEM }; /** @brief A fixed array of compile time privileges */ constexpr std::array basePrivileges{ "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", "ConfigureUsers"}; constexpr const size_t basePrivilegeCount = basePrivileges.size(); /** @brief Max number of privileges per type */ constexpr const size_t maxPrivilegeCount = 32; /** @brief A vector of all privilege names and their indexes */ static const std::array privilegeNames{ "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", "ConfigureUsers"}; /** * @brief Redfish privileges * * This implements a set of Redfish privileges. These directly represent * user privileges and help represent entity privileges. * * 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)) { BMCWEB_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 std::string_view privilege) { for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); searchIndex++) { if (privilege == privilegeNames[searchIndex]) { privilegeBitset.set(searchIndex); return true; } } return false; } /** * @brief Resets the given privilege in the bitset * * @param[in] privilege Privilege to be reset * * @return None * */ bool resetSinglePrivilege(const char* privilege) { for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); searchIndex++) { if (privilege == privilegeNames[searchIndex]) { privilegeBitset.reset(searchIndex); return true; } } return false; } /** * @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 setSinglePrivilege is called, or the Privilege structure is destroyed * */ std::vector getActivePrivilegeNames(const PrivilegeType type) const { std::vector activePrivileges; size_t searchIndex = 0; size_t endIndex = basePrivilegeCount; if (type == PrivilegeType::OEM) { searchIndex = basePrivilegeCount; endIndex = privilegeNames.size(); } for (; searchIndex < endIndex; searchIndex++) { if (privilegeBitset.test(searchIndex)) { activePrivileges.emplace_back(privilegeNames[searchIndex]); } } 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 { return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset; } /** * @brief Returns the intersection of two Privilege sets. * * @param[in] privilege Privilege set to intersect with. * * @return The new Privilege set. * */ Privileges intersection(const Privileges& p) const { return Privileges{privilegeBitset & p.privilegeBitset}; } private: Privileges(const std::bitset& p) : privilegeBitset{p} {} std::bitset privilegeBitset = 0; }; inline const Privileges& getUserPrivileges(const std::string& userRole) { // Redfish privilege : Administrator if (userRole == "priv-admin") { static Privileges admin{"Login", "ConfigureManager", "ConfigureSelf", "ConfigureUsers", "ConfigureComponents"}; return admin; } if (userRole == "priv-operator") { // Redfish privilege : Operator static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"}; return op; } if (userRole == "priv-user") { // Redfish privilege : Readonly static Privileges readOnly{"Login", "ConfigureSelf"}; return readOnly; } // Redfish privilege : NoAccess static Privileges noaccess; return noaccess; } /** * @brief The OperationMap represents the privileges required for a * single entity (URI). It maps from the allowable verbs to the * privileges required to use that operation. * * This represents the Redfish "Privilege AND and OR syntax" as given * in the spec and shown in the Privilege Registry. This does not * implement any Redfish property overrides, subordinate overrides, or * resource URI overrides. This does not implement the limitation of * the ConfigureSelf privilege to operate only on your own account or * session. **/ using OperationMap = boost::container::flat_map>; /* @brief Checks if user is allowed to call an operation * * @param[in] operationPrivilegesRequired Privileges required * @param[in] userPrivileges Privileges the user has * * @return True if operation is allowed, false otherwise */ inline bool isOperationAllowedWithPrivileges( const std::vector& operationPrivilegesRequired, const Privileges& userPrivileges) { // If there are no privileges assigned, there are no privileges required if (operationPrivilegesRequired.empty()) { return true; } for (auto& requiredPrivileges : operationPrivilegesRequired) { BMCWEB_LOG_DEBUG << "Checking operation privileges..."; if (userPrivileges.isSupersetOf(requiredPrivileges)) { BMCWEB_LOG_DEBUG << "...success"; return true; } } return false; } /** * @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 boost::beast::http::verb method, const OperationMap& operationMap, const Privileges& userPrivileges) { const auto& it = operationMap.find(method); if (it == operationMap.end()) { return false; } return isOperationAllowedWithPrivileges(it->second, userPrivileges); } /** * @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 boost::beast::http::verb method, const OperationMap& operationMap, const std::string&) { // 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