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