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 <array> 21 #include <bitset> 22 #include <boost/beast/http/verb.hpp> 23 #include <boost/container/flat_map.hpp> 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::vector<std::string> privilegeNames{basePrivileges.begin(), 48 basePrivileges.end()}; 49 50 /** 51 * @brief Redfish privileges 52 * 53 * Entity privileges and user privileges are represented by this class. 54 * 55 * Each incoming Connection requires a comparison between privileges held 56 * by the user issuing a request and the target entity's privileges. 57 * 58 * To ensure best runtime performance of this comparison, privileges 59 * are represented as bitsets. Each bit in the bitset corresponds to a 60 * unique privilege name. 61 * 62 * A bit is set if the privilege is required (entity domain) or granted 63 * (user domain) and false otherwise. 64 * 65 */ 66 class Privileges 67 { 68 public: 69 /** 70 * @brief Constructs object without any privileges active 71 * 72 */ 73 Privileges() = default; 74 75 /** 76 * @brief Constructs object with given privileges active 77 * 78 * @param[in] privilegeList List of privileges to be activated 79 * 80 */ 81 Privileges(std::initializer_list<const char*> privilegeList) 82 { 83 for (const char* privilege : privilegeList) 84 { 85 if (!setSinglePrivilege(privilege)) 86 { 87 BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege 88 << "in constructor"; 89 } 90 } 91 } 92 93 /** 94 * @brief Sets given privilege in the bitset 95 * 96 * @param[in] privilege Privilege to be set 97 * 98 * @return None 99 * 100 */ 101 bool setSinglePrivilege(const char* privilege) 102 { 103 for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 104 searchIndex++) 105 { 106 if (privilege == privilegeNames[searchIndex]) 107 { 108 privilegeBitset.set(searchIndex); 109 return true; 110 } 111 } 112 113 return false; 114 } 115 116 /** 117 * @brief Sets given privilege in the bitset 118 * 119 * @param[in] privilege Privilege to be set 120 * 121 * @return None 122 * 123 */ 124 bool setSinglePrivilege(const std::string& privilege) 125 { 126 return setSinglePrivilege(privilege.c_str()); 127 } 128 129 /** 130 * @brief Retrieves names of all active privileges for a given type 131 * 132 * @param[in] type Base or OEM 133 * 134 * @return Vector of active privileges. Pointers are valid until 135 * the setSinglePrivilege is called, or the Privilege structure is destroyed 136 * 137 */ 138 std::vector<const std::string*> 139 getActivePrivilegeNames(const PrivilegeType type) const 140 { 141 std::vector<const std::string*> activePrivileges; 142 143 size_t searchIndex = 0; 144 size_t endIndex = basePrivilegeCount; 145 if (type == PrivilegeType::OEM) 146 { 147 searchIndex = basePrivilegeCount - 1; 148 endIndex = privilegeNames.size(); 149 } 150 151 for (; searchIndex < endIndex; searchIndex++) 152 { 153 if (privilegeBitset.test(searchIndex)) 154 { 155 activePrivileges.emplace_back(&privilegeNames[searchIndex]); 156 } 157 } 158 159 return activePrivileges; 160 } 161 162 /** 163 * @brief Determines if this Privilege set is a superset of the given 164 * privilege set 165 * 166 * @param[in] privilege Privilege to be checked 167 * 168 * @return None 169 * 170 */ 171 bool isSupersetOf(const Privileges& p) const 172 { 173 return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset; 174 } 175 176 private: 177 std::bitset<maxPrivilegeCount> privilegeBitset = 0; 178 }; 179 180 inline const Privileges& getUserPrivileges(const std::string& userRole) 181 { 182 // Redfish privilege : Administrator 183 if (userRole == "priv-admin") 184 { 185 static Privileges admin{"Login", "ConfigureManager", "ConfigureSelf", 186 "ConfigureUsers", "ConfigureComponents"}; 187 return admin; 188 } 189 else if (userRole == "priv-operator") 190 { 191 // Redfish privilege : Operator 192 static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"}; 193 return op; 194 } 195 else if (userRole == "priv-user") 196 { 197 // Redfish privilege : Readonly 198 static Privileges readOnly{"Login", "ConfigureSelf"}; 199 return readOnly; 200 } 201 else 202 { 203 // Redfish privilege : NoAccess 204 static Privileges noaccess; 205 return noaccess; 206 } 207 } 208 209 using OperationMap = boost::container::flat_map<boost::beast::http::verb, 210 std::vector<Privileges>>; 211 212 /** 213 * @brief Checks if given privileges allow to call an HTTP method 214 * 215 * @param[in] method HTTP method 216 * @param[in] user Privileges 217 * 218 * @return True if method allowed, false otherwise 219 * 220 */ 221 inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, 222 const OperationMap& operationMap, 223 const Privileges& userPrivileges) 224 { 225 const auto& it = operationMap.find(method); 226 if (it == operationMap.end()) 227 { 228 return false; 229 } 230 231 // If there are no privileges assigned, assume no privileges required 232 if (it->second.empty()) 233 { 234 return true; 235 } 236 237 for (auto& requiredPrivileges : it->second) 238 { 239 if (userPrivileges.isSupersetOf(requiredPrivileges)) 240 { 241 return true; 242 } 243 } 244 return false; 245 } 246 247 /** 248 * @brief Checks if a user is allowed to call an HTTP method 249 * 250 * @param[in] method HTTP method 251 * @param[in] user Username 252 * 253 * @return True if method allowed, false otherwise 254 * 255 */ 256 inline bool isMethodAllowedForUser(const boost::beast::http::verb method, 257 const OperationMap& operationMap, 258 const std::string& user) 259 { 260 // TODO: load user privileges from configuration as soon as its available 261 // now we are granting all privileges to everyone. 262 Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf", 263 "ConfigureUsers", "ConfigureComponents"}; 264 265 return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges); 266 } 267 268 } // namespace redfish 269