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 <boost/beast/http/verb.hpp> 19 #include <boost/container/flat_map.hpp> 20 #include <logging.hpp> 21 22 #include <array> 23 #include <bitset> 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::array<std::string, maxPrivilegeCount> privilegeNames{ 48 "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", 49 "ConfigureUsers"}; 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 " << privilege 90 << "in constructor"; 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(const 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> 150 getActivePrivilegeNames(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 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 if (userRole == "priv-operator") 217 { 218 // Redfish privilege : Operator 219 static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"}; 220 return op; 221 } 222 if (userRole == "priv-user") 223 { 224 // Redfish privilege : Readonly 225 static Privileges readOnly{"Login", "ConfigureSelf"}; 226 return readOnly; 227 } 228 // Redfish privilege : NoAccess 229 static Privileges noaccess; 230 return noaccess; 231 } 232 233 /** 234 * @brief The OperationMap represents the privileges required for a 235 * single entity (URI). It maps from the allowable verbs to the 236 * privileges required to use that operation. 237 * 238 * This represents the Redfish "Privilege AND and OR syntax" as given 239 * in the spec and shown in the Privilege Registry. This does not 240 * implement any Redfish property overrides, subordinate overrides, or 241 * resource URI overrides. This does not implement the limitation of 242 * the ConfigureSelf privilege to operate only on your own account or 243 * session. 244 **/ 245 using OperationMap = boost::container::flat_map<boost::beast::http::verb, 246 std::vector<Privileges>>; 247 248 /* @brief Checks if user is allowed to call an operation 249 * 250 * @param[in] operationPrivilegesRequired Privileges required 251 * @param[in] userPrivileges Privileges the user has 252 * 253 * @return True if operation is allowed, false otherwise 254 */ 255 inline bool isOperationAllowedWithPrivileges( 256 const std::vector<Privileges>& operationPrivilegesRequired, 257 const Privileges& userPrivileges) 258 { 259 // If there are no privileges assigned, there are no privileges required 260 if (operationPrivilegesRequired.empty()) 261 { 262 return true; 263 } 264 for (const auto& requiredPrivileges : operationPrivilegesRequired) 265 { 266 BMCWEB_LOG_DEBUG << "Checking operation privileges..."; 267 if (userPrivileges.isSupersetOf(requiredPrivileges)) 268 { 269 BMCWEB_LOG_DEBUG << "...success"; 270 return true; 271 } 272 } 273 return false; 274 } 275 276 /** 277 * @brief Checks if given privileges allow to call an HTTP method 278 * 279 * @param[in] method HTTP method 280 * @param[in] user Privileges 281 * 282 * @return True if method allowed, false otherwise 283 * 284 */ 285 inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, 286 const OperationMap& operationMap, 287 const Privileges& userPrivileges) 288 { 289 const auto& it = operationMap.find(method); 290 if (it == operationMap.end()) 291 { 292 return false; 293 } 294 295 return isOperationAllowedWithPrivileges(it->second, userPrivileges); 296 } 297 298 } // namespace redfish 299