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