1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation 4 #pragma once 5 6 #include "logging.hpp" 7 #include "sessions.hpp" 8 9 #include <boost/beast/http/verb.hpp> 10 #include <boost/container/flat_map.hpp> 11 #include <boost/container/vector.hpp> 12 13 #include <array> 14 #include <bitset> 15 #include <cstddef> 16 #include <functional> 17 #include <initializer_list> 18 #include <string> 19 #include <string_view> 20 #include <utility> 21 #include <vector> 22 23 namespace redfish 24 { 25 26 enum class PrivilegeType 27 { 28 BASE, 29 OEM 30 }; 31 32 /** @brief A fixed array of compile time privileges */ 33 constexpr std::array<std::string_view, 5> basePrivileges{ 34 "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", 35 "ConfigureUsers"}; 36 37 constexpr const size_t basePrivilegeCount = basePrivileges.size(); 38 39 /** @brief Max number of privileges per type */ 40 constexpr const size_t maxPrivilegeCount = 32; 41 42 /** 43 * @brief A vector of all privilege names and their indexes 44 * The privilege "OpenBMCHostConsole" is added to users who are members of the 45 * "hostconsole" user group. This privilege is required to access the host 46 * console. 47 */ 48 constexpr std::array<std::string_view, maxPrivilegeCount> privilegeNames{ 49 "Login", "ConfigureManager", "ConfigureComponents", 50 "ConfigureSelf", "ConfigureUsers", "OpenBMCHostConsole"}; 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 {} in constructor", 91 privilege); 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(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; 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 explicit Privileges(const std::bitset<maxPrivilegeCount>& p) : 203 privilegeBitset{p} 204 {} 205 std::bitset<maxPrivilegeCount> privilegeBitset = 0; 206 }; 207 208 inline Privileges getUserPrivileges(const persistent_data::UserSession& session) 209 { 210 // default to no access 211 Privileges privs; 212 213 // Check if user is member of hostconsole group 214 for (const auto& userGroup : session.userGroups) 215 { 216 if (userGroup == "hostconsole") 217 { 218 // Redfish privilege : host console access 219 privs.setSinglePrivilege("OpenBMCHostConsole"); 220 break; 221 } 222 } 223 224 if (session.userRole == "priv-admin") 225 { 226 // Redfish privilege : Administrator 227 privs.setSinglePrivilege("Login"); 228 privs.setSinglePrivilege("ConfigureManager"); 229 privs.setSinglePrivilege("ConfigureSelf"); 230 privs.setSinglePrivilege("ConfigureUsers"); 231 privs.setSinglePrivilege("ConfigureComponents"); 232 } 233 else if (session.userRole == "priv-operator") 234 { 235 // Redfish privilege : Operator 236 privs.setSinglePrivilege("Login"); 237 privs.setSinglePrivilege("ConfigureSelf"); 238 privs.setSinglePrivilege("ConfigureComponents"); 239 } 240 else if (session.userRole == "priv-user") 241 { 242 // Redfish privilege : Readonly 243 privs.setSinglePrivilege("Login"); 244 privs.setSinglePrivilege("ConfigureSelf"); 245 } 246 247 return privs; 248 } 249 250 /** 251 * @brief The OperationMap represents the privileges required for a 252 * single entity (URI). It maps from the allowable verbs to the 253 * privileges required to use that operation. 254 * 255 * This represents the Redfish "Privilege AND and OR syntax" as given 256 * in the spec and shown in the Privilege Registry. This does not 257 * implement any Redfish property overrides, subordinate overrides, or 258 * resource URI overrides. This does not implement the limitation of 259 * the ConfigureSelf privilege to operate only on your own account or 260 * session. 261 **/ 262 using OperationMap = boost::container::flat_map<boost::beast::http::verb, 263 std::vector<Privileges>>; 264 265 /* @brief Checks if user is allowed to call an operation 266 * 267 * @param[in] operationPrivilegesRequired Privileges required 268 * @param[in] userPrivileges Privileges the user has 269 * 270 * @return True if operation is allowed, false otherwise 271 */ 272 inline bool isOperationAllowedWithPrivileges( 273 const std::vector<Privileges>& operationPrivilegesRequired, 274 const Privileges& userPrivileges) 275 { 276 // If there are no privileges assigned, there are no privileges required 277 if (operationPrivilegesRequired.empty()) 278 { 279 return true; 280 } 281 for (const auto& requiredPrivileges : operationPrivilegesRequired) 282 { 283 BMCWEB_LOG_DEBUG("Checking operation privileges..."); 284 if (userPrivileges.isSupersetOf(requiredPrivileges)) 285 { 286 BMCWEB_LOG_DEBUG("...success"); 287 return true; 288 } 289 } 290 return false; 291 } 292 293 /** 294 * @brief Checks if given privileges allow to call an HTTP method 295 * 296 * @param[in] method HTTP method 297 * @param[in] user Privileges 298 * 299 * @return True if method allowed, false otherwise 300 * 301 */ 302 inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, 303 const OperationMap& operationMap, 304 const Privileges& userPrivileges) 305 { 306 const auto& it = operationMap.find(method); 307 if (it == operationMap.end()) 308 { 309 return false; 310 } 311 312 return isOperationAllowedWithPrivileges(it->second, userPrivileges); 313 } 314 315 } // namespace redfish 316