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