186e1b661SBorawski.Lukasz /* 286e1b661SBorawski.Lukasz // Copyright (c) 2018 Intel Corporation 386e1b661SBorawski.Lukasz // 486e1b661SBorawski.Lukasz // Licensed under the Apache License, Version 2.0 (the "License"); 586e1b661SBorawski.Lukasz // you may not use this file except in compliance with the License. 686e1b661SBorawski.Lukasz // You may obtain a copy of the License at 786e1b661SBorawski.Lukasz // 886e1b661SBorawski.Lukasz // http://www.apache.org/licenses/LICENSE-2.0 986e1b661SBorawski.Lukasz // 1086e1b661SBorawski.Lukasz // Unless required by applicable law or agreed to in writing, software 1186e1b661SBorawski.Lukasz // distributed under the License is distributed on an "AS IS" BASIS, 1286e1b661SBorawski.Lukasz // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1386e1b661SBorawski.Lukasz // See the License for the specific language governing permissions and 1486e1b661SBorawski.Lukasz // limitations under the License. 1586e1b661SBorawski.Lukasz */ 1686e1b661SBorawski.Lukasz #pragma once 1786e1b661SBorawski.Lukasz 18*aecb47a4SBorawski.Lukasz #include <bitset> 19*aecb47a4SBorawski.Lukasz #include <cstdint> 20*aecb47a4SBorawski.Lukasz #include "crow.h" 21*aecb47a4SBorawski.Lukasz #include <boost/container/flat_map.hpp> 22*aecb47a4SBorawski.Lukasz #include <boost/optional.hpp> 23*aecb47a4SBorawski.Lukasz 2486e1b661SBorawski.Lukasz namespace redfish { 2586e1b661SBorawski.Lukasz 26*aecb47a4SBorawski.Lukasz class PrivilegeProvider; 27*aecb47a4SBorawski.Lukasz 28*aecb47a4SBorawski.Lukasz enum class PrivilegeType { BASE, OEM }; 29*aecb47a4SBorawski.Lukasz 30*aecb47a4SBorawski.Lukasz /** @brief Max number of privileges per type */ 31*aecb47a4SBorawski.Lukasz constexpr const size_t MAX_PRIVILEGE_COUNT = 32; 32*aecb47a4SBorawski.Lukasz using privilegeBitset = std::bitset<MAX_PRIVILEGE_COUNT>; 33*aecb47a4SBorawski.Lukasz 3486e1b661SBorawski.Lukasz /** 35*aecb47a4SBorawski.Lukasz * @brief Redfish privileges 36*aecb47a4SBorawski.Lukasz * 37*aecb47a4SBorawski.Lukasz * Entity privileges and user privileges are represented by this class. 38*aecb47a4SBorawski.Lukasz * 39*aecb47a4SBorawski.Lukasz * Each incoming connection requires a comparison between privileges held 40*aecb47a4SBorawski.Lukasz * by the user issuing a request and the target entity's privileges. 41*aecb47a4SBorawski.Lukasz * 42*aecb47a4SBorawski.Lukasz * To ensure best runtime performance of this comparison, privileges 43*aecb47a4SBorawski.Lukasz * are represented as bitsets. Each bit in the bitset corresponds to a 44*aecb47a4SBorawski.Lukasz * unique privilege name. 45*aecb47a4SBorawski.Lukasz * 46*aecb47a4SBorawski.Lukasz * Privilege names are read from the privilege_registry.json file and 47*aecb47a4SBorawski.Lukasz * stored in flat maps. 48*aecb47a4SBorawski.Lukasz * 49*aecb47a4SBorawski.Lukasz * A bit is set if the privilege is required (entity domain) or granted 50*aecb47a4SBorawski.Lukasz * (user domain) and false otherwise. 51*aecb47a4SBorawski.Lukasz * 52*aecb47a4SBorawski.Lukasz * Bitset index to privilege name mapping depends on the order in which 53*aecb47a4SBorawski.Lukasz * privileges are defined in PrivilegesUsed and OEMPrivilegesUsed arrays 54*aecb47a4SBorawski.Lukasz * in the privilege_registry.json. 5586e1b661SBorawski.Lukasz */ 56*aecb47a4SBorawski.Lukasz class Privileges { 57*aecb47a4SBorawski.Lukasz public: 58*aecb47a4SBorawski.Lukasz /** 59*aecb47a4SBorawski.Lukasz * @brief Retrieves the base privileges bitset 60*aecb47a4SBorawski.Lukasz * 61*aecb47a4SBorawski.Lukasz * @return Bitset representation of base Redfish privileges 62*aecb47a4SBorawski.Lukasz */ 63*aecb47a4SBorawski.Lukasz privilegeBitset getBasePrivilegeBitset() const { return basePrivilegeBitset; } 64*aecb47a4SBorawski.Lukasz 65*aecb47a4SBorawski.Lukasz /** 66*aecb47a4SBorawski.Lukasz * @brief Retrieves the OEM privileges bitset 67*aecb47a4SBorawski.Lukasz * 68*aecb47a4SBorawski.Lukasz * @return Bitset representation of OEM Redfish privileges 69*aecb47a4SBorawski.Lukasz */ 70*aecb47a4SBorawski.Lukasz privilegeBitset getOEMPrivilegeBitset() const { return oemPrivilegeBitset; } 71*aecb47a4SBorawski.Lukasz 72*aecb47a4SBorawski.Lukasz /** 73*aecb47a4SBorawski.Lukasz * @brief Sets given privilege in the bitset 74*aecb47a4SBorawski.Lukasz * 75*aecb47a4SBorawski.Lukasz * @param[in] privilege Privilege to be set 76*aecb47a4SBorawski.Lukasz * 77*aecb47a4SBorawski.Lukasz * @return None 78*aecb47a4SBorawski.Lukasz */ 79*aecb47a4SBorawski.Lukasz void setSinglePrivilege(const std::string& privilege) { 80*aecb47a4SBorawski.Lukasz auto index = getBitsetIndexForPrivilege(privilege, PrivilegeType::BASE); 81*aecb47a4SBorawski.Lukasz if (index) { 82*aecb47a4SBorawski.Lukasz basePrivilegeBitset.set(*index); 83*aecb47a4SBorawski.Lukasz return; 84*aecb47a4SBorawski.Lukasz } 85*aecb47a4SBorawski.Lukasz 86*aecb47a4SBorawski.Lukasz index = getBitsetIndexForPrivilege(privilege, PrivilegeType::OEM); 87*aecb47a4SBorawski.Lukasz if (index) { 88*aecb47a4SBorawski.Lukasz oemPrivilegeBitset.set(*index); 89*aecb47a4SBorawski.Lukasz } 90*aecb47a4SBorawski.Lukasz } 91*aecb47a4SBorawski.Lukasz 92*aecb47a4SBorawski.Lukasz /** 93*aecb47a4SBorawski.Lukasz * @brief Retrieves names of all active privileges for a given type 94*aecb47a4SBorawski.Lukasz * 95*aecb47a4SBorawski.Lukasz * @param[in] type Base or OEM 96*aecb47a4SBorawski.Lukasz * 97*aecb47a4SBorawski.Lukasz * @return Vector of active privileges 98*aecb47a4SBorawski.Lukasz */ 99*aecb47a4SBorawski.Lukasz std::vector<std::string> getActivePrivilegeNames( 100*aecb47a4SBorawski.Lukasz const PrivilegeType type) const { 101*aecb47a4SBorawski.Lukasz std::vector<std::string> activePrivileges; 102*aecb47a4SBorawski.Lukasz 103*aecb47a4SBorawski.Lukasz if (type == PrivilegeType::BASE) { 104*aecb47a4SBorawski.Lukasz for (const auto& pair : basePrivNameToIndexMap) { 105*aecb47a4SBorawski.Lukasz if (basePrivilegeBitset.test(pair.second)) { 106*aecb47a4SBorawski.Lukasz activePrivileges.emplace_back(pair.first); 107*aecb47a4SBorawski.Lukasz } 108*aecb47a4SBorawski.Lukasz } 109*aecb47a4SBorawski.Lukasz } else { 110*aecb47a4SBorawski.Lukasz for (const auto& pair : oemPrivNameToIndexMap) { 111*aecb47a4SBorawski.Lukasz if (oemPrivilegeBitset.test(pair.second)) { 112*aecb47a4SBorawski.Lukasz activePrivileges.emplace_back(pair.first); 113*aecb47a4SBorawski.Lukasz } 114*aecb47a4SBorawski.Lukasz } 115*aecb47a4SBorawski.Lukasz } 116*aecb47a4SBorawski.Lukasz 117*aecb47a4SBorawski.Lukasz return activePrivileges; 118*aecb47a4SBorawski.Lukasz } 119*aecb47a4SBorawski.Lukasz 12086e1b661SBorawski.Lukasz private: 121*aecb47a4SBorawski.Lukasz boost::optional<size_t> getBitsetIndexForPrivilege( 122*aecb47a4SBorawski.Lukasz const std::string& privilege, const PrivilegeType type) const { 123*aecb47a4SBorawski.Lukasz if (type == PrivilegeType::BASE) { 124*aecb47a4SBorawski.Lukasz const auto pair = basePrivNameToIndexMap.find(privilege); 125*aecb47a4SBorawski.Lukasz if (pair != basePrivNameToIndexMap.end()) { 126*aecb47a4SBorawski.Lukasz return pair->second; 127*aecb47a4SBorawski.Lukasz } 128*aecb47a4SBorawski.Lukasz } else { 129*aecb47a4SBorawski.Lukasz const auto pair = oemPrivNameToIndexMap.find(privilege); 130*aecb47a4SBorawski.Lukasz if (pair != oemPrivNameToIndexMap.end()) { 131*aecb47a4SBorawski.Lukasz return pair->second; 132*aecb47a4SBorawski.Lukasz } 133*aecb47a4SBorawski.Lukasz } 134*aecb47a4SBorawski.Lukasz 135*aecb47a4SBorawski.Lukasz return boost::none; 136*aecb47a4SBorawski.Lukasz } 137*aecb47a4SBorawski.Lukasz 138*aecb47a4SBorawski.Lukasz privilegeBitset basePrivilegeBitset; 139*aecb47a4SBorawski.Lukasz privilegeBitset oemPrivilegeBitset; 140*aecb47a4SBorawski.Lukasz 141*aecb47a4SBorawski.Lukasz static boost::container::flat_map<std::string, size_t> basePrivNameToIndexMap; 142*aecb47a4SBorawski.Lukasz static boost::container::flat_map<std::string, size_t> oemPrivNameToIndexMap; 143*aecb47a4SBorawski.Lukasz 144*aecb47a4SBorawski.Lukasz friend class PrivilegeProvider; 14586e1b661SBorawski.Lukasz }; 14686e1b661SBorawski.Lukasz 14786e1b661SBorawski.Lukasz /** 148*aecb47a4SBorawski.Lukasz * @brief Class used to store privileges for Redfish entities 14986e1b661SBorawski.Lukasz */ 15086e1b661SBorawski.Lukasz class EntityPrivileges { 15186e1b661SBorawski.Lukasz public: 152*aecb47a4SBorawski.Lukasz /** 153*aecb47a4SBorawski.Lukasz * @brief Checks if a user is allowed to call an HTTP method 154*aecb47a4SBorawski.Lukasz * 155*aecb47a4SBorawski.Lukasz * @param[in] method HTTP method 156*aecb47a4SBorawski.Lukasz * @param[in] user Username 157*aecb47a4SBorawski.Lukasz * 158*aecb47a4SBorawski.Lukasz * @return True if method allowed, false otherwise 159*aecb47a4SBorawski.Lukasz */ 160*aecb47a4SBorawski.Lukasz bool isMethodAllowedForUser(const crow::HTTPMethod method, 161*aecb47a4SBorawski.Lukasz const std::string& user) const; 162*aecb47a4SBorawski.Lukasz 163*aecb47a4SBorawski.Lukasz /** 164*aecb47a4SBorawski.Lukasz * @brief Checks if given privileges allow to call an HTTP method 165*aecb47a4SBorawski.Lukasz * 166*aecb47a4SBorawski.Lukasz * @param[in] method HTTP method 167*aecb47a4SBorawski.Lukasz * @param[in] user Privileges 168*aecb47a4SBorawski.Lukasz * 169*aecb47a4SBorawski.Lukasz * @return True if method allowed, false otherwise 170*aecb47a4SBorawski.Lukasz */ 171*aecb47a4SBorawski.Lukasz bool isMethodAllowedWithPrivileges(const crow::HTTPMethod method, 172*aecb47a4SBorawski.Lukasz const Privileges& userPrivileges) const; 173*aecb47a4SBorawski.Lukasz 174*aecb47a4SBorawski.Lukasz /** 175*aecb47a4SBorawski.Lukasz * @brief Sets required privileges for a method on a given entity 176*aecb47a4SBorawski.Lukasz * 177*aecb47a4SBorawski.Lukasz * @param[in] method HTTP method 178*aecb47a4SBorawski.Lukasz * @param[in] privileges Required privileges 179*aecb47a4SBorawski.Lukasz * 180*aecb47a4SBorawski.Lukasz * @return None 181*aecb47a4SBorawski.Lukasz */ 182*aecb47a4SBorawski.Lukasz void addPrivilegesRequiredByMethod(const crow::HTTPMethod method, 183*aecb47a4SBorawski.Lukasz const Privileges& privileges) { 184*aecb47a4SBorawski.Lukasz methodToPrivilegeMap[method].push_back(privileges); 18586e1b661SBorawski.Lukasz } 186*aecb47a4SBorawski.Lukasz 187*aecb47a4SBorawski.Lukasz private: 188*aecb47a4SBorawski.Lukasz bool verifyPrivileges(const privilegeBitset userPrivilegeBitset, 189*aecb47a4SBorawski.Lukasz const privilegeBitset requiredPrivilegeBitset) const; 190*aecb47a4SBorawski.Lukasz 191*aecb47a4SBorawski.Lukasz boost::container::flat_map<crow::HTTPMethod, std::vector<Privileges>> 192*aecb47a4SBorawski.Lukasz methodToPrivilegeMap; 19386e1b661SBorawski.Lukasz }; 19486e1b661SBorawski.Lukasz 19586e1b661SBorawski.Lukasz /** 19686e1b661SBorawski.Lukasz * @brief Class used to: 197*aecb47a4SBorawski.Lukasz * - read the privilege_registry.json file 198*aecb47a4SBorawski.Lukasz * - provide EntityPrivileges objects to callers 19986e1b661SBorawski.Lukasz * 200*aecb47a4SBorawski.Lukasz * To save runtime memory, object of this class should 201*aecb47a4SBorawski.Lukasz * exist only for the time required to install all Nodes 20286e1b661SBorawski.Lukasz */ 20386e1b661SBorawski.Lukasz class PrivilegeProvider { 20486e1b661SBorawski.Lukasz public: 205*aecb47a4SBorawski.Lukasz PrivilegeProvider(const std::string& privilegeRegistryPath) { 206*aecb47a4SBorawski.Lukasz // TODO: read this path from the configuration once its available 207*aecb47a4SBorawski.Lukasz std::ifstream privilegeRegistryFile{privilegeRegistryPath}; 208*aecb47a4SBorawski.Lukasz 209*aecb47a4SBorawski.Lukasz if (privilegeRegistryFile.is_open()) { 210*aecb47a4SBorawski.Lukasz if (!loadPrivilegesFromFile(privilegeRegistryFile)) { 211*aecb47a4SBorawski.Lukasz privilegeRegistryJson.clear(); 212*aecb47a4SBorawski.Lukasz CROW_LOG_ERROR << "Couldn't parse privilege_registry.json"; 213*aecb47a4SBorawski.Lukasz } 214*aecb47a4SBorawski.Lukasz } else { 215*aecb47a4SBorawski.Lukasz CROW_LOG_ERROR << "Couldn't open privilege_registry.json"; 216*aecb47a4SBorawski.Lukasz } 21786e1b661SBorawski.Lukasz } 21886e1b661SBorawski.Lukasz 219*aecb47a4SBorawski.Lukasz /** 220*aecb47a4SBorawski.Lukasz * @brief Gets required privileges for a certain entity type 221*aecb47a4SBorawski.Lukasz * 222*aecb47a4SBorawski.Lukasz * @param[in] entityUrl Entity url 223*aecb47a4SBorawski.Lukasz * @param[in] entityType Entity type 224*aecb47a4SBorawski.Lukasz * 225*aecb47a4SBorawski.Lukasz * @return EntityPrivilege object 226*aecb47a4SBorawski.Lukasz */ 227*aecb47a4SBorawski.Lukasz EntityPrivileges getPrivilegesRequiredByEntity( 228*aecb47a4SBorawski.Lukasz const std::string& entityUrl, const std::string& entityType) const; 229*aecb47a4SBorawski.Lukasz 230*aecb47a4SBorawski.Lukasz private: 231*aecb47a4SBorawski.Lukasz bool loadPrivilegesFromFile(std::ifstream& privilegeRegistryFile); 232*aecb47a4SBorawski.Lukasz bool privilegeRegistryHasRequiredFields() const; 233*aecb47a4SBorawski.Lukasz bool parseOperationMap(const nlohmann::json& operationMap, 234*aecb47a4SBorawski.Lukasz EntityPrivileges& entityPrivileges) const; 235*aecb47a4SBorawski.Lukasz bool fillPrivilegeMap(const nlohmann::json& privilegesUsed, 236*aecb47a4SBorawski.Lukasz boost::container::flat_map<std::string, size_t>& 237*aecb47a4SBorawski.Lukasz privilegeToIndexMap) const; 238*aecb47a4SBorawski.Lukasz 239*aecb47a4SBorawski.Lukasz nlohmann::json privilegeRegistryJson; 24086e1b661SBorawski.Lukasz }; 24186e1b661SBorawski.Lukasz 24286e1b661SBorawski.Lukasz } // namespace redfish 24386e1b661SBorawski.Lukasz 244