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