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 <logging.h> 19 20 #include <array> 21 #include <bitset> 22 #include <boost/beast/http/verb.hpp> 23 #include <boost/container/flat_map.hpp> 24 #include <cstdint> 25 #include <vector> 26 27 namespace redfish 28 { 29 30 enum class PrivilegeType 31 { 32 BASE, 33 OEM 34 }; 35 36 /** @brief A fixed array of compile time privileges */ 37 constexpr std::array<const char*, 5> basePrivileges{ 38 "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", 39 "ConfigureUsers"}; 40 41 constexpr const size_t basePrivilegeCount = basePrivileges.size(); 42 43 /** @brief Max number of privileges per type */ 44 constexpr const size_t maxPrivilegeCount = 32; 45 46 /** @brief A vector of all privilege names and their indexes */ 47 static const std::vector<std::string> privilegeNames{basePrivileges.begin(), 48 basePrivileges.end()}; 49 50 /** 51 * @brief Redfish privileges 52 * 53 * This implements a set of Redfish privileges. These directly represent 54 * user privileges and help represent entity privileges. 55 * 56 * Each incoming Connection requires a comparison between privileges held 57 * by the user issuing a request and the target entity's privileges. 58 * 59 * To ensure best runtime performance of this comparison, privileges 60 * are represented as bitsets. Each bit in the bitset corresponds to a 61 * unique privilege name. 62 * 63 * A bit is set if the privilege is required (entity domain) or granted 64 * (user domain) and false otherwise. 65 * 66 */ 67 class Privileges 68 { 69 public: 70 /** 71 * @brief Constructs object without any privileges active 72 * 73 */ 74 Privileges() = default; 75 76 /** 77 * @brief Constructs object with given privileges active 78 * 79 * @param[in] privilegeList List of privileges to be activated 80 * 81 */ 82 Privileges(std::initializer_list<const char*> privilegeList) 83 { 84 for (const char* privilege : privilegeList) 85 { 86 if (!setSinglePrivilege(privilege)) 87 { 88 BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege 89 << "in constructor"; 90 } 91 } 92 } 93 94 /** 95 * @brief Sets given privilege in the bitset 96 * 97 * @param[in] privilege Privilege to be set 98 * 99 * @return None 100 * 101 */ 102 bool setSinglePrivilege(const char* privilege) 103 { 104 for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 105 searchIndex++) 106 { 107 if (privilege == privilegeNames[searchIndex]) 108 { 109 privilegeBitset.set(searchIndex); 110 return true; 111 } 112 } 113 114 return false; 115 } 116 117 /** 118 * @brief Sets given privilege in the bitset 119 * 120 * @param[in] privilege Privilege to be set 121 * 122 * @return None 123 * 124 */ 125 bool setSinglePrivilege(const std::string& privilege) 126 { 127 return setSinglePrivilege(privilege.c_str()); 128 } 129 130 /** 131 * @brief Resets the given privilege in the bitset 132 * 133 * @param[in] privilege Privilege to be reset 134 * 135 * @return None 136 * 137 */ 138 bool resetSinglePrivilege(const char* privilege) 139 { 140 for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 141 searchIndex++) 142 { 143 if (privilege == privilegeNames[searchIndex]) 144 { 145 privilegeBitset.reset(searchIndex); 146 return true; 147 } 148 } 149 return false; 150 } 151 152 /** 153 * @brief Retrieves names of all active privileges for a given type 154 * 155 * @param[in] type Base or OEM 156 * 157 * @return Vector of active privileges. Pointers are valid until 158 * the setSinglePrivilege is called, or the Privilege structure is destroyed 159 * 160 */ 161 std::vector<const std::string*> 162 getActivePrivilegeNames(const PrivilegeType type) const 163 { 164 std::vector<const std::string*> activePrivileges; 165 166 size_t searchIndex = 0; 167 size_t endIndex = basePrivilegeCount; 168 if (type == PrivilegeType::OEM) 169 { 170 searchIndex = basePrivilegeCount - 1; 171 endIndex = privilegeNames.size(); 172 } 173 174 for (; searchIndex < endIndex; searchIndex++) 175 { 176 if (privilegeBitset.test(searchIndex)) 177 { 178 activePrivileges.emplace_back(&privilegeNames[searchIndex]); 179 } 180 } 181 182 return activePrivileges; 183 } 184 185 /** 186 * @brief Determines if this Privilege set is a superset of the given 187 * privilege set 188 * 189 * @param[in] privilege Privilege to be checked 190 * 191 * @return None 192 * 193 */ 194 bool isSupersetOf(const Privileges& p) const 195 { 196 return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset; 197 } 198 199 /** 200 * @brief Returns the intersection of two Privilege sets. 201 * 202 * @param[in] privilege Privilege set to intersect with. 203 * 204 * @return The new Privilege set. 205 * 206 */ 207 Privileges intersection(const Privileges& p) const 208 { 209 return Privileges{privilegeBitset & p.privilegeBitset}; 210 } 211 212 private: 213 Privileges(const std::bitset<maxPrivilegeCount>& p) : privilegeBitset{p} 214 { 215 } 216 std::bitset<maxPrivilegeCount> privilegeBitset = 0; 217 }; 218 219 inline const Privileges& getUserPrivileges(const std::string& userRole) 220 { 221 // Redfish privilege : Administrator 222 if (userRole == "priv-admin") 223 { 224 static Privileges admin{"Login", "ConfigureManager", "ConfigureSelf", 225 "ConfigureUsers", "ConfigureComponents"}; 226 return admin; 227 } 228 else if (userRole == "priv-operator") 229 { 230 // Redfish privilege : Operator 231 static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"}; 232 return op; 233 } 234 else if (userRole == "priv-user") 235 { 236 // Redfish privilege : Readonly 237 static Privileges readOnly{"Login", "ConfigureSelf"}; 238 return readOnly; 239 } 240 else 241 { 242 // Redfish privilege : NoAccess 243 static Privileges noaccess; 244 return noaccess; 245 } 246 } 247 248 /** 249 * @brief The OperationMap represents the privileges required for a 250 * single entity (URI). It maps from the allowable verbs to the 251 * privileges required to use that operation. 252 * 253 * This represents the Redfish "Privilege AND and OR syntax" as given 254 * in the spec and shown in the Privilege Registry. This does not 255 * implement any Redfish property overrides, subordinate overrides, or 256 * resource URI overrides. This does not implement the limitation of 257 * the ConfigureSelf privilege to operate only on your own account or 258 * session. 259 **/ 260 using OperationMap = boost::container::flat_map<boost::beast::http::verb, 261 std::vector<Privileges>>; 262 263 /* @brief Checks if user is allowed to call an operation 264 * 265 * @param[in] operationPrivilegesRequired Privileges required 266 * @param[in] userPrivileges Privileges the user has 267 * 268 * @return True if operation is allowed, false otherwise 269 */ 270 inline bool isOperationAllowedWithPrivileges( 271 const std::vector<Privileges>& operationPrivilegesRequired, 272 const Privileges& userPrivileges) 273 { 274 // If there are no privileges assigned, there are no privileges required 275 if (operationPrivilegesRequired.empty()) 276 { 277 return true; 278 } 279 for (auto& requiredPrivileges : operationPrivilegesRequired) 280 { 281 BMCWEB_LOG_ERROR << "Checking operation privileges..."; 282 if (userPrivileges.isSupersetOf(requiredPrivileges)) 283 { 284 BMCWEB_LOG_ERROR << "...success"; 285 return true; 286 } 287 } 288 return false; 289 } 290 291 /** 292 * @brief Checks if given privileges allow to call an HTTP method 293 * 294 * @param[in] method HTTP method 295 * @param[in] user Privileges 296 * 297 * @return True if method allowed, false otherwise 298 * 299 */ 300 inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, 301 const OperationMap& operationMap, 302 const Privileges& userPrivileges) 303 { 304 const auto& it = operationMap.find(method); 305 if (it == operationMap.end()) 306 { 307 return false; 308 } 309 310 return isOperationAllowedWithPrivileges(it->second, userPrivileges); 311 } 312 313 /** 314 * @brief Checks if a user is allowed to call an HTTP method 315 * 316 * @param[in] method HTTP method 317 * @param[in] user Username 318 * 319 * @return True if method allowed, false otherwise 320 * 321 */ 322 inline bool isMethodAllowedForUser(const boost::beast::http::verb method, 323 const OperationMap& operationMap, 324 const std::string& user) 325 { 326 // TODO: load user privileges from configuration as soon as its available 327 // now we are granting all privileges to everyone. 328 Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf", 329 "ConfigureUsers", "ConfigureComponents"}; 330 331 return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges); 332 } 333 334 } // namespace redfish 335