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