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