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 <crow/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 196 { 197 // Redfish privilege : Readonly 198 static Privileges readOnly{"Login", "ConfigureSelf"}; 199 return readOnly; 200 } 201 } 202 203 using OperationMap = boost::container::flat_map<boost::beast::http::verb, 204 std::vector<Privileges>>; 205 206 /** 207 * @brief Checks if given privileges allow to call an HTTP method 208 * 209 * @param[in] method HTTP method 210 * @param[in] user Privileges 211 * 212 * @return True if method allowed, false otherwise 213 * 214 */ 215 inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, 216 const OperationMap& operationMap, 217 const Privileges& userPrivileges) 218 { 219 const auto& it = operationMap.find(method); 220 if (it == operationMap.end()) 221 { 222 return false; 223 } 224 225 // If there are no privileges assigned, assume no privileges required 226 if (it->second.empty()) 227 { 228 return true; 229 } 230 231 for (auto& requiredPrivileges : it->second) 232 { 233 if (userPrivileges.isSupersetOf(requiredPrivileges)) 234 { 235 return true; 236 } 237 } 238 return false; 239 } 240 241 /** 242 * @brief Checks if a user is allowed to call an HTTP method 243 * 244 * @param[in] method HTTP method 245 * @param[in] user Username 246 * 247 * @return True if method allowed, false otherwise 248 * 249 */ 250 inline bool isMethodAllowedForUser(const boost::beast::http::verb method, 251 const OperationMap& operationMap, 252 const std::string& user) 253 { 254 // TODO: load user privileges from configuration as soon as its available 255 // now we are granting all privileges to everyone. 256 Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf", 257 "ConfigureUsers", "ConfigureComponents"}; 258 259 return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges); 260 } 261 262 } // namespace redfish 263