186e1b661SBorawski.Lukasz /* 286e1b661SBorawski.Lukasz // Copyright (c) 2018 Intel Corporation 386e1b661SBorawski.Lukasz // 486e1b661SBorawski.Lukasz // Licensed under the Apache License, Version 2.0 (the "License"); 586e1b661SBorawski.Lukasz // you may not use this file except in compliance with the License. 686e1b661SBorawski.Lukasz // You may obtain a copy of the License at 786e1b661SBorawski.Lukasz // 886e1b661SBorawski.Lukasz // http://www.apache.org/licenses/LICENSE-2.0 986e1b661SBorawski.Lukasz // 1086e1b661SBorawski.Lukasz // Unless required by applicable law or agreed to in writing, software 1186e1b661SBorawski.Lukasz // distributed under the License is distributed on an "AS IS" BASIS, 1286e1b661SBorawski.Lukasz // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1386e1b661SBorawski.Lukasz // See the License for the specific language governing permissions and 1486e1b661SBorawski.Lukasz // limitations under the License. 1586e1b661SBorawski.Lukasz */ 1686e1b661SBorawski.Lukasz #pragma once 1786e1b661SBorawski.Lukasz 18aecb47a4SBorawski.Lukasz #include <bitset> 19aecb47a4SBorawski.Lukasz #include <cstdint> 20aecb47a4SBorawski.Lukasz #include "crow.h" 21aecb47a4SBorawski.Lukasz #include <boost/container/flat_map.hpp> 22*43a095abSBorawski.Lukasz #include <boost/container/flat_set.hpp> 23aecb47a4SBorawski.Lukasz #include <boost/optional.hpp> 24aecb47a4SBorawski.Lukasz 2586e1b661SBorawski.Lukasz namespace redfish { 2686e1b661SBorawski.Lukasz 27aecb47a4SBorawski.Lukasz class PrivilegeProvider; 28aecb47a4SBorawski.Lukasz 29aecb47a4SBorawski.Lukasz enum class PrivilegeType { BASE, OEM }; 30aecb47a4SBorawski.Lukasz 31aecb47a4SBorawski.Lukasz /** @brief Max number of privileges per type */ 32aecb47a4SBorawski.Lukasz constexpr const size_t MAX_PRIVILEGE_COUNT = 32; 33*43a095abSBorawski.Lukasz 34aecb47a4SBorawski.Lukasz using privilegeBitset = std::bitset<MAX_PRIVILEGE_COUNT>; 35aecb47a4SBorawski.Lukasz 36*43a095abSBorawski.Lukasz /** @brief Number of mappings must be <= MAX_PRIVILEGE_COUNT */ 37*43a095abSBorawski.Lukasz static const boost::container::flat_map<std::string, size_t> 38*43a095abSBorawski.Lukasz basePrivNameToIndexMap = {{"Login", 0}, 39*43a095abSBorawski.Lukasz {"ConfigureManager", 1}, 40*43a095abSBorawski.Lukasz {"ConfigureComponents", 2}, 41*43a095abSBorawski.Lukasz {"ConfigureSelf", 3}, 42*43a095abSBorawski.Lukasz {"ConfigureUsers", 4}}; 43*43a095abSBorawski.Lukasz 44*43a095abSBorawski.Lukasz /** @brief Number of mappings must be <= MAX_PRIVILEGE_COUNT */ 45*43a095abSBorawski.Lukasz static const boost::container::flat_map<std::string, size_t> 46*43a095abSBorawski.Lukasz oemPrivNameToIndexMap = {}; 47*43a095abSBorawski.Lukasz 4886e1b661SBorawski.Lukasz /** 49aecb47a4SBorawski.Lukasz * @brief Redfish privileges 50aecb47a4SBorawski.Lukasz * 51aecb47a4SBorawski.Lukasz * Entity privileges and user privileges are represented by this class. 52aecb47a4SBorawski.Lukasz * 53aecb47a4SBorawski.Lukasz * Each incoming connection requires a comparison between privileges held 54aecb47a4SBorawski.Lukasz * by the user issuing a request and the target entity's privileges. 55aecb47a4SBorawski.Lukasz * 56aecb47a4SBorawski.Lukasz * To ensure best runtime performance of this comparison, privileges 57aecb47a4SBorawski.Lukasz * are represented as bitsets. Each bit in the bitset corresponds to a 58aecb47a4SBorawski.Lukasz * unique privilege name. 59aecb47a4SBorawski.Lukasz * 60aecb47a4SBorawski.Lukasz * A bit is set if the privilege is required (entity domain) or granted 61aecb47a4SBorawski.Lukasz * (user domain) and false otherwise. 62aecb47a4SBorawski.Lukasz * 6386e1b661SBorawski.Lukasz */ 64aecb47a4SBorawski.Lukasz class Privileges { 65aecb47a4SBorawski.Lukasz public: 66aecb47a4SBorawski.Lukasz /** 67*43a095abSBorawski.Lukasz * @brief Constructs object without any privileges active 68*43a095abSBorawski.Lukasz * 69*43a095abSBorawski.Lukasz */ 70*43a095abSBorawski.Lukasz Privileges() = default; 71*43a095abSBorawski.Lukasz 72*43a095abSBorawski.Lukasz /** 73*43a095abSBorawski.Lukasz * @brief Constructs object with given privileges active 74*43a095abSBorawski.Lukasz * 75*43a095abSBorawski.Lukasz * @param[in] privilegeList List of privileges to be activated 76*43a095abSBorawski.Lukasz * 77*43a095abSBorawski.Lukasz */ 78*43a095abSBorawski.Lukasz Privileges(std::initializer_list<std::string> privilegeList) { 79*43a095abSBorawski.Lukasz for (const auto& privilege : privilegeList) { 80*43a095abSBorawski.Lukasz setSinglePrivilege(privilege); 81*43a095abSBorawski.Lukasz } 82*43a095abSBorawski.Lukasz } 83*43a095abSBorawski.Lukasz 84*43a095abSBorawski.Lukasz /** 85aecb47a4SBorawski.Lukasz * @brief Retrieves the base privileges bitset 86aecb47a4SBorawski.Lukasz * 87aecb47a4SBorawski.Lukasz * @return Bitset representation of base Redfish privileges 88*43a095abSBorawski.Lukasz * 89aecb47a4SBorawski.Lukasz */ 90aecb47a4SBorawski.Lukasz privilegeBitset getBasePrivilegeBitset() const { return basePrivilegeBitset; } 91aecb47a4SBorawski.Lukasz 92aecb47a4SBorawski.Lukasz /** 93aecb47a4SBorawski.Lukasz * @brief Retrieves the OEM privileges bitset 94aecb47a4SBorawski.Lukasz * 95aecb47a4SBorawski.Lukasz * @return Bitset representation of OEM Redfish privileges 96*43a095abSBorawski.Lukasz * 97aecb47a4SBorawski.Lukasz */ 98aecb47a4SBorawski.Lukasz privilegeBitset getOEMPrivilegeBitset() const { return oemPrivilegeBitset; } 99aecb47a4SBorawski.Lukasz 100aecb47a4SBorawski.Lukasz /** 101aecb47a4SBorawski.Lukasz * @brief Sets given privilege in the bitset 102aecb47a4SBorawski.Lukasz * 103aecb47a4SBorawski.Lukasz * @param[in] privilege Privilege to be set 104aecb47a4SBorawski.Lukasz * 105aecb47a4SBorawski.Lukasz * @return None 106*43a095abSBorawski.Lukasz * 107aecb47a4SBorawski.Lukasz */ 108aecb47a4SBorawski.Lukasz void setSinglePrivilege(const std::string& privilege) { 109aecb47a4SBorawski.Lukasz auto index = getBitsetIndexForPrivilege(privilege, PrivilegeType::BASE); 110aecb47a4SBorawski.Lukasz if (index) { 111aecb47a4SBorawski.Lukasz basePrivilegeBitset.set(*index); 112aecb47a4SBorawski.Lukasz return; 113aecb47a4SBorawski.Lukasz } 114aecb47a4SBorawski.Lukasz 115aecb47a4SBorawski.Lukasz index = getBitsetIndexForPrivilege(privilege, PrivilegeType::OEM); 116aecb47a4SBorawski.Lukasz if (index) { 117aecb47a4SBorawski.Lukasz oemPrivilegeBitset.set(*index); 118aecb47a4SBorawski.Lukasz } 119aecb47a4SBorawski.Lukasz } 120aecb47a4SBorawski.Lukasz 121aecb47a4SBorawski.Lukasz /** 122aecb47a4SBorawski.Lukasz * @brief Retrieves names of all active privileges for a given type 123aecb47a4SBorawski.Lukasz * 124aecb47a4SBorawski.Lukasz * @param[in] type Base or OEM 125aecb47a4SBorawski.Lukasz * 126aecb47a4SBorawski.Lukasz * @return Vector of active privileges 127*43a095abSBorawski.Lukasz * 128aecb47a4SBorawski.Lukasz */ 129aecb47a4SBorawski.Lukasz std::vector<std::string> getActivePrivilegeNames( 130aecb47a4SBorawski.Lukasz const PrivilegeType type) const { 131aecb47a4SBorawski.Lukasz std::vector<std::string> activePrivileges; 132aecb47a4SBorawski.Lukasz 133aecb47a4SBorawski.Lukasz if (type == PrivilegeType::BASE) { 134aecb47a4SBorawski.Lukasz for (const auto& pair : basePrivNameToIndexMap) { 135aecb47a4SBorawski.Lukasz if (basePrivilegeBitset.test(pair.second)) { 136aecb47a4SBorawski.Lukasz activePrivileges.emplace_back(pair.first); 137aecb47a4SBorawski.Lukasz } 138aecb47a4SBorawski.Lukasz } 139aecb47a4SBorawski.Lukasz } else { 140aecb47a4SBorawski.Lukasz for (const auto& pair : oemPrivNameToIndexMap) { 141aecb47a4SBorawski.Lukasz if (oemPrivilegeBitset.test(pair.second)) { 142aecb47a4SBorawski.Lukasz activePrivileges.emplace_back(pair.first); 143aecb47a4SBorawski.Lukasz } 144aecb47a4SBorawski.Lukasz } 145aecb47a4SBorawski.Lukasz } 146aecb47a4SBorawski.Lukasz 147aecb47a4SBorawski.Lukasz return activePrivileges; 148aecb47a4SBorawski.Lukasz } 149aecb47a4SBorawski.Lukasz 15086e1b661SBorawski.Lukasz private: 151aecb47a4SBorawski.Lukasz boost::optional<size_t> getBitsetIndexForPrivilege( 152aecb47a4SBorawski.Lukasz const std::string& privilege, const PrivilegeType type) const { 153aecb47a4SBorawski.Lukasz if (type == PrivilegeType::BASE) { 154aecb47a4SBorawski.Lukasz const auto pair = basePrivNameToIndexMap.find(privilege); 155aecb47a4SBorawski.Lukasz if (pair != basePrivNameToIndexMap.end()) { 156aecb47a4SBorawski.Lukasz return pair->second; 157aecb47a4SBorawski.Lukasz } 158aecb47a4SBorawski.Lukasz } else { 159aecb47a4SBorawski.Lukasz const auto pair = oemPrivNameToIndexMap.find(privilege); 160aecb47a4SBorawski.Lukasz if (pair != oemPrivNameToIndexMap.end()) { 161aecb47a4SBorawski.Lukasz return pair->second; 162aecb47a4SBorawski.Lukasz } 163aecb47a4SBorawski.Lukasz } 164aecb47a4SBorawski.Lukasz 165aecb47a4SBorawski.Lukasz return boost::none; 166aecb47a4SBorawski.Lukasz } 167aecb47a4SBorawski.Lukasz 168aecb47a4SBorawski.Lukasz privilegeBitset basePrivilegeBitset; 169aecb47a4SBorawski.Lukasz privilegeBitset oemPrivilegeBitset; 170aecb47a4SBorawski.Lukasz 171aecb47a4SBorawski.Lukasz friend class PrivilegeProvider; 17286e1b661SBorawski.Lukasz }; 17386e1b661SBorawski.Lukasz 174*43a095abSBorawski.Lukasz using OperationMap = 175*43a095abSBorawski.Lukasz boost::container::flat_map<crow::HTTPMethod, std::vector<Privileges>>; 176*43a095abSBorawski.Lukasz 177*43a095abSBorawski.Lukasz /** 178*43a095abSBorawski.Lukasz * @brief Class used to store overrides privileges for Redfish 179*43a095abSBorawski.Lukasz * entities 180*43a095abSBorawski.Lukasz * 181*43a095abSBorawski.Lukasz */ 182*43a095abSBorawski.Lukasz class EntityPrivilegesOverride { 183*43a095abSBorawski.Lukasz protected: 184*43a095abSBorawski.Lukasz /** 185*43a095abSBorawski.Lukasz * @brief Constructs overrides object for given targets 186*43a095abSBorawski.Lukasz * 187*43a095abSBorawski.Lukasz * @param[in] operationMap Operation map to be applied for targets 188*43a095abSBorawski.Lukasz * @param[in] targets List of targets whOperation map to be applied for 189*43a095abSBorawski.Lukasz * targets 190*43a095abSBorawski.Lukasz * 191*43a095abSBorawski.Lukasz */ 192*43a095abSBorawski.Lukasz EntityPrivilegesOverride(OperationMap&& operationMap, 193*43a095abSBorawski.Lukasz std::initializer_list<std::string>&& targets) 194*43a095abSBorawski.Lukasz : operationMap(std::move(operationMap)), targets(std::move(targets)) {} 195*43a095abSBorawski.Lukasz 196*43a095abSBorawski.Lukasz const OperationMap operationMap; 197*43a095abSBorawski.Lukasz const boost::container::flat_set<std::string> targets; 198*43a095abSBorawski.Lukasz }; 199*43a095abSBorawski.Lukasz 200*43a095abSBorawski.Lukasz class PropertyOverride : public EntityPrivilegesOverride { 201*43a095abSBorawski.Lukasz public: 202*43a095abSBorawski.Lukasz PropertyOverride(OperationMap&& operationMap, 203*43a095abSBorawski.Lukasz std::initializer_list<std::string>&& targets) 204*43a095abSBorawski.Lukasz : EntityPrivilegesOverride(std::move(operationMap), std::move(targets)) {} 205*43a095abSBorawski.Lukasz }; 206*43a095abSBorawski.Lukasz 207*43a095abSBorawski.Lukasz class SubordinateOverride : public EntityPrivilegesOverride { 208*43a095abSBorawski.Lukasz public: 209*43a095abSBorawski.Lukasz SubordinateOverride(OperationMap&& operationMap, 210*43a095abSBorawski.Lukasz std::initializer_list<std::string>&& targets) 211*43a095abSBorawski.Lukasz : EntityPrivilegesOverride(std::move(operationMap), std::move(targets)) {} 212*43a095abSBorawski.Lukasz }; 213*43a095abSBorawski.Lukasz 214*43a095abSBorawski.Lukasz class ResourceURIOverride : public EntityPrivilegesOverride { 215*43a095abSBorawski.Lukasz public: 216*43a095abSBorawski.Lukasz ResourceURIOverride(OperationMap&& operationMap, 217*43a095abSBorawski.Lukasz std::initializer_list<std::string>&& targets) 218*43a095abSBorawski.Lukasz : EntityPrivilegesOverride(std::move(operationMap), std::move(targets)) {} 219*43a095abSBorawski.Lukasz }; 220*43a095abSBorawski.Lukasz 22186e1b661SBorawski.Lukasz /** 222aecb47a4SBorawski.Lukasz * @brief Class used to store privileges for Redfish entities 223*43a095abSBorawski.Lukasz * 22486e1b661SBorawski.Lukasz */ 22586e1b661SBorawski.Lukasz class EntityPrivileges { 22686e1b661SBorawski.Lukasz public: 227aecb47a4SBorawski.Lukasz /** 228*43a095abSBorawski.Lukasz * @brief Constructor for default case with no overrides 229*43a095abSBorawski.Lukasz * 230*43a095abSBorawski.Lukasz * @param[in] operationMap Operation map for the entity 231*43a095abSBorawski.Lukasz * 232*43a095abSBorawski.Lukasz */ 233*43a095abSBorawski.Lukasz EntityPrivileges(OperationMap&& operationMap) 234*43a095abSBorawski.Lukasz : operationMap(std::move(operationMap)) {} 235*43a095abSBorawski.Lukasz 236*43a095abSBorawski.Lukasz /** 237*43a095abSBorawski.Lukasz * @brief Constructors for overrides 238*43a095abSBorawski.Lukasz * 239*43a095abSBorawski.Lukasz * @param[in] operationMap Default operation map for the entity 240*43a095abSBorawski.Lukasz * @param[in] propertyOverrides Vector of property overrides 241*43a095abSBorawski.Lukasz * @param[in] subordinateOverrides Vector of subordinate overrides 242*43a095abSBorawski.Lukasz * @param[in] resourceURIOverrides Vector of resource URI overrides 243*43a095abSBorawski.Lukasz * 244*43a095abSBorawski.Lukasz */ 245*43a095abSBorawski.Lukasz EntityPrivileges(OperationMap&& operationMap, 246*43a095abSBorawski.Lukasz std::vector<PropertyOverride>&& propertyOverrides, 247*43a095abSBorawski.Lukasz std::vector<SubordinateOverride>&& subordinateOverrides, 248*43a095abSBorawski.Lukasz std::vector<ResourceURIOverride>&& resourceURIOverrides) 249*43a095abSBorawski.Lukasz : operationMap(std::move(operationMap)), 250*43a095abSBorawski.Lukasz propertyOverrides(std::move(propertyOverrides)), 251*43a095abSBorawski.Lukasz subordinateOverrides(std::move(subordinateOverrides)), 252*43a095abSBorawski.Lukasz resourceURIOverrides(std::move(resourceURIOverrides)) {} 253*43a095abSBorawski.Lukasz 254*43a095abSBorawski.Lukasz /** 255aecb47a4SBorawski.Lukasz * @brief Checks if a user is allowed to call an HTTP method 256aecb47a4SBorawski.Lukasz * 257aecb47a4SBorawski.Lukasz * @param[in] method HTTP method 258aecb47a4SBorawski.Lukasz * @param[in] user Username 259aecb47a4SBorawski.Lukasz * 260aecb47a4SBorawski.Lukasz * @return True if method allowed, false otherwise 261*43a095abSBorawski.Lukasz * 262aecb47a4SBorawski.Lukasz */ 263aecb47a4SBorawski.Lukasz bool isMethodAllowedForUser(const crow::HTTPMethod method, 264*43a095abSBorawski.Lukasz const std::string& user) const { 265*43a095abSBorawski.Lukasz // TODO: load user privileges from configuration as soon as its available 266*43a095abSBorawski.Lukasz // now we are granting all privileges to everyone. 267*43a095abSBorawski.Lukasz auto userPrivileges = 268*43a095abSBorawski.Lukasz Privileges{"Login", "ConfigureManager", "ConfigureSelf", 269*43a095abSBorawski.Lukasz "ConfigureUsers", "ConfigureComponents"}; 270*43a095abSBorawski.Lukasz 271*43a095abSBorawski.Lukasz return isMethodAllowedWithPrivileges(method, userPrivileges); 272*43a095abSBorawski.Lukasz } 273aecb47a4SBorawski.Lukasz 274aecb47a4SBorawski.Lukasz /** 275aecb47a4SBorawski.Lukasz * @brief Checks if given privileges allow to call an HTTP method 276aecb47a4SBorawski.Lukasz * 277aecb47a4SBorawski.Lukasz * @param[in] method HTTP method 278aecb47a4SBorawski.Lukasz * @param[in] user Privileges 279aecb47a4SBorawski.Lukasz * 280aecb47a4SBorawski.Lukasz * @return True if method allowed, false otherwise 281*43a095abSBorawski.Lukasz * 282aecb47a4SBorawski.Lukasz */ 283aecb47a4SBorawski.Lukasz bool isMethodAllowedWithPrivileges(const crow::HTTPMethod method, 284*43a095abSBorawski.Lukasz const Privileges& userPrivileges) const { 285*43a095abSBorawski.Lukasz if (operationMap.find(method) == operationMap.end()) { 286*43a095abSBorawski.Lukasz return false; 287*43a095abSBorawski.Lukasz } 288aecb47a4SBorawski.Lukasz 289*43a095abSBorawski.Lukasz for (auto& requiredPrivileges : operationMap.at(method)) { 290*43a095abSBorawski.Lukasz // Check if user has required base privileges 291*43a095abSBorawski.Lukasz if (!verifyPrivileges(userPrivileges.getBasePrivilegeBitset(), 292*43a095abSBorawski.Lukasz requiredPrivileges.getBasePrivilegeBitset())) { 293*43a095abSBorawski.Lukasz continue; 294*43a095abSBorawski.Lukasz } 295*43a095abSBorawski.Lukasz 296*43a095abSBorawski.Lukasz // Check if user has required OEM privileges 297*43a095abSBorawski.Lukasz if (!verifyPrivileges(userPrivileges.getOEMPrivilegeBitset(), 298*43a095abSBorawski.Lukasz requiredPrivileges.getOEMPrivilegeBitset())) { 299*43a095abSBorawski.Lukasz continue; 300*43a095abSBorawski.Lukasz } 301*43a095abSBorawski.Lukasz 302*43a095abSBorawski.Lukasz return true; 303*43a095abSBorawski.Lukasz } 304*43a095abSBorawski.Lukasz return false; 30586e1b661SBorawski.Lukasz } 306aecb47a4SBorawski.Lukasz 307aecb47a4SBorawski.Lukasz private: 308aecb47a4SBorawski.Lukasz bool verifyPrivileges(const privilegeBitset userPrivilegeBitset, 309*43a095abSBorawski.Lukasz const privilegeBitset requiredPrivilegeBitset) const { 310*43a095abSBorawski.Lukasz return (userPrivilegeBitset & requiredPrivilegeBitset) == 311*43a095abSBorawski.Lukasz requiredPrivilegeBitset; 31286e1b661SBorawski.Lukasz } 31386e1b661SBorawski.Lukasz 314*43a095abSBorawski.Lukasz OperationMap operationMap; 315aecb47a4SBorawski.Lukasz 316*43a095abSBorawski.Lukasz // Overrides are not implemented at the moment. 317*43a095abSBorawski.Lukasz std::vector<PropertyOverride> propertyOverrides; 318*43a095abSBorawski.Lukasz std::vector<SubordinateOverride> subordinateOverrides; 319*43a095abSBorawski.Lukasz std::vector<ResourceURIOverride> resourceURIOverrides; 32086e1b661SBorawski.Lukasz }; 32186e1b661SBorawski.Lukasz 32286e1b661SBorawski.Lukasz } // namespace redfish 32386e1b661SBorawski.Lukasz 324