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