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