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