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/container/flat_set.hpp> 23 #include <boost/optional.hpp> 24 25 namespace redfish { 26 27 class PrivilegeProvider; 28 29 enum class PrivilegeType { BASE, OEM }; 30 31 /** @brief Max number of privileges per type */ 32 constexpr const size_t MAX_PRIVILEGE_COUNT = 32; 33 34 using privilegeBitset = std::bitset<MAX_PRIVILEGE_COUNT>; 35 36 /** @brief Number of mappings must be <= MAX_PRIVILEGE_COUNT */ 37 static const boost::container::flat_map<std::string, size_t> 38 basePrivNameToIndexMap = {{"Login", 0}, 39 {"ConfigureManager", 1}, 40 {"ConfigureComponents", 2}, 41 {"ConfigureSelf", 3}, 42 {"ConfigureUsers", 4}}; 43 44 /** @brief Number of mappings must be <= MAX_PRIVILEGE_COUNT */ 45 static const boost::container::flat_map<std::string, size_t> 46 oemPrivNameToIndexMap = {}; 47 48 /** 49 * @brief Redfish privileges 50 * 51 * Entity privileges and user privileges are represented by this class. 52 * 53 * Each incoming connection requires a comparison between privileges held 54 * by the user issuing a request and the target entity's privileges. 55 * 56 * To ensure best runtime performance of this comparison, privileges 57 * are represented as bitsets. Each bit in the bitset corresponds to a 58 * unique privilege name. 59 * 60 * A bit is set if the privilege is required (entity domain) or granted 61 * (user domain) and false otherwise. 62 * 63 */ 64 class Privileges { 65 public: 66 /** 67 * @brief Constructs object without any privileges active 68 * 69 */ 70 Privileges() = default; 71 72 /** 73 * @brief Constructs object with given privileges active 74 * 75 * @param[in] privilegeList List of privileges to be activated 76 * 77 */ 78 Privileges(std::initializer_list<std::string> privilegeList) { 79 for (const auto& privilege : privilegeList) { 80 setSinglePrivilege(privilege); 81 } 82 } 83 84 /** 85 * @brief Retrieves the base privileges bitset 86 * 87 * @return Bitset representation of base Redfish privileges 88 * 89 */ 90 privilegeBitset getBasePrivilegeBitset() const { return basePrivilegeBitset; } 91 92 /** 93 * @brief Retrieves the OEM privileges bitset 94 * 95 * @return Bitset representation of OEM Redfish privileges 96 * 97 */ 98 privilegeBitset getOEMPrivilegeBitset() const { return oemPrivilegeBitset; } 99 100 /** 101 * @brief Sets given privilege in the bitset 102 * 103 * @param[in] privilege Privilege to be set 104 * 105 * @return None 106 * 107 */ 108 void setSinglePrivilege(const std::string& privilege) { 109 auto index = getBitsetIndexForPrivilege(privilege, PrivilegeType::BASE); 110 if (index) { 111 basePrivilegeBitset.set(*index); 112 return; 113 } 114 115 index = getBitsetIndexForPrivilege(privilege, PrivilegeType::OEM); 116 if (index) { 117 oemPrivilegeBitset.set(*index); 118 } 119 } 120 121 /** 122 * @brief Retrieves names of all active privileges for a given type 123 * 124 * @param[in] type Base or OEM 125 * 126 * @return Vector of active privileges 127 * 128 */ 129 std::vector<std::string> getActivePrivilegeNames( 130 const PrivilegeType type) const { 131 std::vector<std::string> activePrivileges; 132 133 if (type == PrivilegeType::BASE) { 134 for (const auto& pair : basePrivNameToIndexMap) { 135 if (basePrivilegeBitset.test(pair.second)) { 136 activePrivileges.emplace_back(pair.first); 137 } 138 } 139 } else { 140 for (const auto& pair : oemPrivNameToIndexMap) { 141 if (oemPrivilegeBitset.test(pair.second)) { 142 activePrivileges.emplace_back(pair.first); 143 } 144 } 145 } 146 147 return activePrivileges; 148 } 149 150 private: 151 boost::optional<size_t> getBitsetIndexForPrivilege( 152 const std::string& privilege, const PrivilegeType type) const { 153 if (type == PrivilegeType::BASE) { 154 const auto pair = basePrivNameToIndexMap.find(privilege); 155 if (pair != basePrivNameToIndexMap.end()) { 156 return pair->second; 157 } 158 } else { 159 const auto pair = oemPrivNameToIndexMap.find(privilege); 160 if (pair != oemPrivNameToIndexMap.end()) { 161 return pair->second; 162 } 163 } 164 165 return boost::none; 166 } 167 168 privilegeBitset basePrivilegeBitset; 169 privilegeBitset oemPrivilegeBitset; 170 171 friend class PrivilegeProvider; 172 }; 173 174 using OperationMap = 175 boost::container::flat_map<crow::HTTPMethod, std::vector<Privileges>>; 176 177 /** 178 * @brief Class used to store overrides privileges for Redfish 179 * entities 180 * 181 */ 182 class EntityPrivilegesOverride { 183 protected: 184 /** 185 * @brief Constructs overrides object for given targets 186 * 187 * @param[in] operationMap Operation map to be applied for targets 188 * @param[in] targets List of targets whOperation map to be applied for 189 * targets 190 * 191 */ 192 EntityPrivilegesOverride(OperationMap&& operationMap, 193 std::initializer_list<std::string>&& targets) 194 : operationMap(std::move(operationMap)), targets(std::move(targets)) {} 195 196 const OperationMap operationMap; 197 const boost::container::flat_set<std::string> targets; 198 }; 199 200 class PropertyOverride : public EntityPrivilegesOverride { 201 public: 202 PropertyOverride(OperationMap&& operationMap, 203 std::initializer_list<std::string>&& targets) 204 : EntityPrivilegesOverride(std::move(operationMap), std::move(targets)) {} 205 }; 206 207 class SubordinateOverride : public EntityPrivilegesOverride { 208 public: 209 SubordinateOverride(OperationMap&& operationMap, 210 std::initializer_list<std::string>&& targets) 211 : EntityPrivilegesOverride(std::move(operationMap), std::move(targets)) {} 212 }; 213 214 class ResourceURIOverride : public EntityPrivilegesOverride { 215 public: 216 ResourceURIOverride(OperationMap&& operationMap, 217 std::initializer_list<std::string>&& targets) 218 : EntityPrivilegesOverride(std::move(operationMap), std::move(targets)) {} 219 }; 220 221 /** 222 * @brief Class used to store privileges for Redfish entities 223 * 224 */ 225 class EntityPrivileges { 226 public: 227 /** 228 * @brief Constructor for default case with no overrides 229 * 230 * @param[in] operationMap Operation map for the entity 231 * 232 */ 233 EntityPrivileges(OperationMap&& operationMap) 234 : operationMap(std::move(operationMap)) {} 235 236 /** 237 * @brief Constructors for overrides 238 * 239 * @param[in] operationMap Default operation map for the entity 240 * @param[in] propertyOverrides Vector of property overrides 241 * @param[in] subordinateOverrides Vector of subordinate overrides 242 * @param[in] resourceURIOverrides Vector of resource URI overrides 243 * 244 */ 245 EntityPrivileges(OperationMap&& operationMap, 246 std::vector<PropertyOverride>&& propertyOverrides, 247 std::vector<SubordinateOverride>&& subordinateOverrides, 248 std::vector<ResourceURIOverride>&& resourceURIOverrides) 249 : operationMap(std::move(operationMap)), 250 propertyOverrides(std::move(propertyOverrides)), 251 subordinateOverrides(std::move(subordinateOverrides)), 252 resourceURIOverrides(std::move(resourceURIOverrides)) {} 253 254 /** 255 * @brief Checks if a user is allowed to call an HTTP method 256 * 257 * @param[in] method HTTP method 258 * @param[in] user Username 259 * 260 * @return True if method allowed, false otherwise 261 * 262 */ 263 bool isMethodAllowedForUser(const crow::HTTPMethod method, 264 const std::string& user) const { 265 // TODO: load user privileges from configuration as soon as its available 266 // now we are granting all privileges to everyone. 267 auto userPrivileges = 268 Privileges{"Login", "ConfigureManager", "ConfigureSelf", 269 "ConfigureUsers", "ConfigureComponents"}; 270 271 return isMethodAllowedWithPrivileges(method, userPrivileges); 272 } 273 274 /** 275 * @brief Checks if given privileges allow to call an HTTP method 276 * 277 * @param[in] method HTTP method 278 * @param[in] user Privileges 279 * 280 * @return True if method allowed, false otherwise 281 * 282 */ 283 bool isMethodAllowedWithPrivileges(const crow::HTTPMethod method, 284 const Privileges& userPrivileges) const { 285 if (operationMap.find(method) == operationMap.end()) { 286 return false; 287 } 288 289 for (auto& requiredPrivileges : operationMap.at(method)) { 290 // Check if user has required base privileges 291 if (!verifyPrivileges(userPrivileges.getBasePrivilegeBitset(), 292 requiredPrivileges.getBasePrivilegeBitset())) { 293 continue; 294 } 295 296 // Check if user has required OEM privileges 297 if (!verifyPrivileges(userPrivileges.getOEMPrivilegeBitset(), 298 requiredPrivileges.getOEMPrivilegeBitset())) { 299 continue; 300 } 301 302 return true; 303 } 304 return false; 305 } 306 307 private: 308 bool verifyPrivileges(const privilegeBitset userPrivilegeBitset, 309 const privilegeBitset requiredPrivilegeBitset) const { 310 return (userPrivilegeBitset & requiredPrivilegeBitset) == 311 requiredPrivilegeBitset; 312 } 313 314 OperationMap operationMap; 315 316 // Overrides are not implemented at the moment. 317 std::vector<PropertyOverride> propertyOverrides; 318 std::vector<SubordinateOverride> subordinateOverrides; 319 std::vector<ResourceURIOverride> resourceURIOverrides; 320 }; 321 322 } // namespace redfish 323 324