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 18f00032dbSTanous #include <boost/beast/http/verb.hpp> 19aecb47a4SBorawski.Lukasz #include <boost/container/flat_map.hpp> 2004e438cbSEd Tanous #include <logging.hpp> 211214b7e7SGunnar Mills 221214b7e7SGunnar Mills #include <array> 231214b7e7SGunnar Mills #include <bitset> 241abe55efSEd Tanous #include <cstdint> 251abe55efSEd Tanous #include <vector> 26aecb47a4SBorawski.Lukasz 271abe55efSEd Tanous namespace redfish 281abe55efSEd Tanous { 291abe55efSEd Tanous 301abe55efSEd Tanous enum class PrivilegeType 311abe55efSEd Tanous { 321abe55efSEd Tanous BASE, 331abe55efSEd Tanous OEM 341abe55efSEd Tanous }; 35aecb47a4SBorawski.Lukasz 36a692779fSEd Tanous /** @brief A fixed array of compile time privileges */ 37a692779fSEd Tanous constexpr std::array<const char*, 5> basePrivileges{ 383ebd75f7SEd Tanous "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", 393ebd75f7SEd Tanous "ConfigureUsers"}; 4043a095abSBorawski.Lukasz 41271584abSEd Tanous constexpr const size_t basePrivilegeCount = basePrivileges.size(); 42a692779fSEd Tanous 43a692779fSEd Tanous /** @brief Max number of privileges per type */ 44271584abSEd Tanous constexpr const size_t maxPrivilegeCount = 32; 45a692779fSEd Tanous 46a692779fSEd Tanous /** @brief A vector of all privilege names and their indexes */ 4723a21a1cSEd Tanous static const std::array<std::string, maxPrivilegeCount> privilegeNames{ 4823a21a1cSEd Tanous "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", 4923a21a1cSEd Tanous "ConfigureUsers"}; 5043a095abSBorawski.Lukasz 5186e1b661SBorawski.Lukasz /** 52aecb47a4SBorawski.Lukasz * @brief Redfish privileges 53aecb47a4SBorawski.Lukasz * 54900f9497SJoseph Reynolds * This implements a set of Redfish privileges. These directly represent 55900f9497SJoseph Reynolds * user privileges and help represent entity privileges. 56aecb47a4SBorawski.Lukasz * 5755c7b7a2SEd Tanous * Each incoming Connection requires a comparison between privileges held 58aecb47a4SBorawski.Lukasz * by the user issuing a request and the target entity's privileges. 59aecb47a4SBorawski.Lukasz * 60aecb47a4SBorawski.Lukasz * To ensure best runtime performance of this comparison, privileges 61aecb47a4SBorawski.Lukasz * are represented as bitsets. Each bit in the bitset corresponds to a 62aecb47a4SBorawski.Lukasz * unique privilege name. 63aecb47a4SBorawski.Lukasz * 64aecb47a4SBorawski.Lukasz * A bit is set if the privilege is required (entity domain) or granted 65aecb47a4SBorawski.Lukasz * (user domain) and false otherwise. 66aecb47a4SBorawski.Lukasz * 6786e1b661SBorawski.Lukasz */ 681abe55efSEd Tanous class Privileges 691abe55efSEd Tanous { 70aecb47a4SBorawski.Lukasz public: 71aecb47a4SBorawski.Lukasz /** 7243a095abSBorawski.Lukasz * @brief Constructs object without any privileges active 7343a095abSBorawski.Lukasz * 7443a095abSBorawski.Lukasz */ 7543a095abSBorawski.Lukasz Privileges() = default; 7643a095abSBorawski.Lukasz 7743a095abSBorawski.Lukasz /** 7843a095abSBorawski.Lukasz * @brief Constructs object with given privileges active 7943a095abSBorawski.Lukasz * 8043a095abSBorawski.Lukasz * @param[in] privilegeList List of privileges to be activated 8143a095abSBorawski.Lukasz * 8243a095abSBorawski.Lukasz */ 831abe55efSEd Tanous Privileges(std::initializer_list<const char*> privilegeList) 841abe55efSEd Tanous { 851abe55efSEd Tanous for (const char* privilege : privilegeList) 861abe55efSEd Tanous { 871abe55efSEd Tanous if (!setSinglePrivilege(privilege)) 881abe55efSEd Tanous { 8955c7b7a2SEd Tanous BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege 903ebd75f7SEd Tanous << "in constructor"; 9143a095abSBorawski.Lukasz } 9243a095abSBorawski.Lukasz } 933ebd75f7SEd Tanous } 94aecb47a4SBorawski.Lukasz 95aecb47a4SBorawski.Lukasz /** 96aecb47a4SBorawski.Lukasz * @brief Sets given privilege in the bitset 97aecb47a4SBorawski.Lukasz * 98aecb47a4SBorawski.Lukasz * @param[in] privilege Privilege to be set 99aecb47a4SBorawski.Lukasz * 100aecb47a4SBorawski.Lukasz * @return None 10143a095abSBorawski.Lukasz * 102aecb47a4SBorawski.Lukasz */ 10323a21a1cSEd Tanous bool setSinglePrivilege(const std::string_view privilege) 1041abe55efSEd Tanous { 105271584abSEd Tanous for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 1061abe55efSEd Tanous searchIndex++) 1071abe55efSEd Tanous { 1081abe55efSEd Tanous if (privilege == privilegeNames[searchIndex]) 1091abe55efSEd Tanous { 11055c7b7a2SEd Tanous privilegeBitset.set(searchIndex); 1113ebd75f7SEd Tanous return true; 112aecb47a4SBorawski.Lukasz } 113a692779fSEd Tanous } 114aecb47a4SBorawski.Lukasz 1153ebd75f7SEd Tanous return false; 1163ebd75f7SEd Tanous } 1173ebd75f7SEd Tanous 1183ebd75f7SEd Tanous /** 119900f9497SJoseph Reynolds * @brief Resets the given privilege in the bitset 120900f9497SJoseph Reynolds * 121900f9497SJoseph Reynolds * @param[in] privilege Privilege to be reset 122900f9497SJoseph Reynolds * 123900f9497SJoseph Reynolds * @return None 124900f9497SJoseph Reynolds * 125900f9497SJoseph Reynolds */ 126900f9497SJoseph Reynolds bool resetSinglePrivilege(const char* privilege) 127900f9497SJoseph Reynolds { 128900f9497SJoseph Reynolds for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 129900f9497SJoseph Reynolds searchIndex++) 130900f9497SJoseph Reynolds { 131900f9497SJoseph Reynolds if (privilege == privilegeNames[searchIndex]) 132900f9497SJoseph Reynolds { 133900f9497SJoseph Reynolds privilegeBitset.reset(searchIndex); 134900f9497SJoseph Reynolds return true; 135900f9497SJoseph Reynolds } 136900f9497SJoseph Reynolds } 137900f9497SJoseph Reynolds return false; 138900f9497SJoseph Reynolds } 139900f9497SJoseph Reynolds 140900f9497SJoseph Reynolds /** 141aecb47a4SBorawski.Lukasz * @brief Retrieves names of all active privileges for a given type 142aecb47a4SBorawski.Lukasz * 143aecb47a4SBorawski.Lukasz * @param[in] type Base or OEM 144aecb47a4SBorawski.Lukasz * 1453ebd75f7SEd Tanous * @return Vector of active privileges. Pointers are valid until 146a692779fSEd Tanous * the setSinglePrivilege is called, or the Privilege structure is destroyed 14743a095abSBorawski.Lukasz * 148aecb47a4SBorawski.Lukasz */ 14923a21a1cSEd Tanous std::vector<std::string> 1501abe55efSEd Tanous getActivePrivilegeNames(const PrivilegeType type) const 1511abe55efSEd Tanous { 15223a21a1cSEd Tanous std::vector<std::string> activePrivileges; 153aecb47a4SBorawski.Lukasz 154271584abSEd Tanous size_t searchIndex = 0; 155271584abSEd Tanous size_t endIndex = basePrivilegeCount; 1561abe55efSEd Tanous if (type == PrivilegeType::OEM) 1571abe55efSEd Tanous { 15887704464SJoseph Reynolds searchIndex = basePrivilegeCount; 15955c7b7a2SEd Tanous endIndex = privilegeNames.size(); 160a692779fSEd Tanous } 161a692779fSEd Tanous 1621abe55efSEd Tanous for (; searchIndex < endIndex; searchIndex++) 1631abe55efSEd Tanous { 1641abe55efSEd Tanous if (privilegeBitset.test(searchIndex)) 1651abe55efSEd Tanous { 16623a21a1cSEd Tanous activePrivileges.emplace_back(privilegeNames[searchIndex]); 167aecb47a4SBorawski.Lukasz } 168aecb47a4SBorawski.Lukasz } 169a692779fSEd Tanous 170aecb47a4SBorawski.Lukasz return activePrivileges; 171aecb47a4SBorawski.Lukasz } 172aecb47a4SBorawski.Lukasz 1733ebd75f7SEd Tanous /** 1743ebd75f7SEd Tanous * @brief Determines if this Privilege set is a superset of the given 1753ebd75f7SEd Tanous * privilege set 1763ebd75f7SEd Tanous * 1773ebd75f7SEd Tanous * @param[in] privilege Privilege to be checked 1783ebd75f7SEd Tanous * 1793ebd75f7SEd Tanous * @return None 1803ebd75f7SEd Tanous * 1813ebd75f7SEd Tanous */ 1821abe55efSEd Tanous bool isSupersetOf(const Privileges& p) const 1831abe55efSEd Tanous { 184a692779fSEd Tanous return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset; 1853ebd75f7SEd Tanous } 1863ebd75f7SEd Tanous 1873bf4e632SJoseph Reynolds /** 1883bf4e632SJoseph Reynolds * @brief Returns the intersection of two Privilege sets. 1893bf4e632SJoseph Reynolds * 1903bf4e632SJoseph Reynolds * @param[in] privilege Privilege set to intersect with. 1913bf4e632SJoseph Reynolds * 1923bf4e632SJoseph Reynolds * @return The new Privilege set. 1933bf4e632SJoseph Reynolds * 1943bf4e632SJoseph Reynolds */ 1953bf4e632SJoseph Reynolds Privileges intersection(const Privileges& p) const 1963bf4e632SJoseph Reynolds { 1973bf4e632SJoseph Reynolds return Privileges{privilegeBitset & p.privilegeBitset}; 1983bf4e632SJoseph Reynolds } 1993bf4e632SJoseph Reynolds 20086e1b661SBorawski.Lukasz private: 201*4e23a444SEd Tanous explicit Privileges(const std::bitset<maxPrivilegeCount>& p) : 202*4e23a444SEd Tanous privilegeBitset{p} 2031214b7e7SGunnar Mills {} 20455c7b7a2SEd Tanous std::bitset<maxPrivilegeCount> privilegeBitset = 0; 20586e1b661SBorawski.Lukasz }; 20686e1b661SBorawski.Lukasz 2076f359568SRatan Gupta inline const Privileges& getUserPrivileges(const std::string& userRole) 2086f359568SRatan Gupta { 2096f359568SRatan Gupta // Redfish privilege : Administrator 2106f359568SRatan Gupta if (userRole == "priv-admin") 2116f359568SRatan Gupta { 2126f359568SRatan Gupta static Privileges admin{"Login", "ConfigureManager", "ConfigureSelf", 2136f359568SRatan Gupta "ConfigureUsers", "ConfigureComponents"}; 2146f359568SRatan Gupta return admin; 2156f359568SRatan Gupta } 2163174e4dfSEd Tanous if (userRole == "priv-operator") 2176f359568SRatan Gupta { 2186f359568SRatan Gupta // Redfish privilege : Operator 2196f359568SRatan Gupta static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"}; 2206f359568SRatan Gupta return op; 2216f359568SRatan Gupta } 2223174e4dfSEd Tanous if (userRole == "priv-user") 2236f359568SRatan Gupta { 2246f359568SRatan Gupta // Redfish privilege : Readonly 2256f359568SRatan Gupta static Privileges readOnly{"Login", "ConfigureSelf"}; 2266f359568SRatan Gupta return readOnly; 2276f359568SRatan Gupta } 228d7e08029Sjayaprakash Mutyala // Redfish privilege : NoAccess 229d7e08029Sjayaprakash Mutyala static Privileges noaccess; 230d7e08029Sjayaprakash Mutyala return noaccess; 231d7e08029Sjayaprakash Mutyala } 2326f359568SRatan Gupta 233900f9497SJoseph Reynolds /** 234900f9497SJoseph Reynolds * @brief The OperationMap represents the privileges required for a 235900f9497SJoseph Reynolds * single entity (URI). It maps from the allowable verbs to the 236900f9497SJoseph Reynolds * privileges required to use that operation. 237900f9497SJoseph Reynolds * 238900f9497SJoseph Reynolds * This represents the Redfish "Privilege AND and OR syntax" as given 239900f9497SJoseph Reynolds * in the spec and shown in the Privilege Registry. This does not 240900f9497SJoseph Reynolds * implement any Redfish property overrides, subordinate overrides, or 241900f9497SJoseph Reynolds * resource URI overrides. This does not implement the limitation of 242900f9497SJoseph Reynolds * the ConfigureSelf privilege to operate only on your own account or 243900f9497SJoseph Reynolds * session. 244900f9497SJoseph Reynolds **/ 245e0d918bcSEd Tanous using OperationMap = boost::container::flat_map<boost::beast::http::verb, 246e0d918bcSEd Tanous std::vector<Privileges>>; 24743a095abSBorawski.Lukasz 248900f9497SJoseph Reynolds /* @brief Checks if user is allowed to call an operation 249900f9497SJoseph Reynolds * 250900f9497SJoseph Reynolds * @param[in] operationPrivilegesRequired Privileges required 251900f9497SJoseph Reynolds * @param[in] userPrivileges Privileges the user has 252900f9497SJoseph Reynolds * 253900f9497SJoseph Reynolds * @return True if operation is allowed, false otherwise 254900f9497SJoseph Reynolds */ 255900f9497SJoseph Reynolds inline bool isOperationAllowedWithPrivileges( 256900f9497SJoseph Reynolds const std::vector<Privileges>& operationPrivilegesRequired, 257900f9497SJoseph Reynolds const Privileges& userPrivileges) 258900f9497SJoseph Reynolds { 259900f9497SJoseph Reynolds // If there are no privileges assigned, there are no privileges required 260900f9497SJoseph Reynolds if (operationPrivilegesRequired.empty()) 261900f9497SJoseph Reynolds { 262900f9497SJoseph Reynolds return true; 263900f9497SJoseph Reynolds } 2649eb808c1SEd Tanous for (const auto& requiredPrivileges : operationPrivilegesRequired) 265900f9497SJoseph Reynolds { 26654fbf177SAndrew Geissler BMCWEB_LOG_DEBUG << "Checking operation privileges..."; 267900f9497SJoseph Reynolds if (userPrivileges.isSupersetOf(requiredPrivileges)) 268900f9497SJoseph Reynolds { 26954fbf177SAndrew Geissler BMCWEB_LOG_DEBUG << "...success"; 270900f9497SJoseph Reynolds return true; 271900f9497SJoseph Reynolds } 272900f9497SJoseph Reynolds } 273900f9497SJoseph Reynolds return false; 274900f9497SJoseph Reynolds } 275900f9497SJoseph Reynolds 27643a095abSBorawski.Lukasz /** 277aecb47a4SBorawski.Lukasz * @brief Checks if given privileges allow to call an HTTP method 278aecb47a4SBorawski.Lukasz * 279aecb47a4SBorawski.Lukasz * @param[in] method HTTP method 280aecb47a4SBorawski.Lukasz * @param[in] user Privileges 281aecb47a4SBorawski.Lukasz * 282aecb47a4SBorawski.Lukasz * @return True if method allowed, false otherwise 28343a095abSBorawski.Lukasz * 284aecb47a4SBorawski.Lukasz */ 285e0d918bcSEd Tanous inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, 2863ebd75f7SEd Tanous const OperationMap& operationMap, 2871abe55efSEd Tanous const Privileges& userPrivileges) 2881abe55efSEd Tanous { 2893ebd75f7SEd Tanous const auto& it = operationMap.find(method); 2901abe55efSEd Tanous if (it == operationMap.end()) 2911abe55efSEd Tanous { 29243a095abSBorawski.Lukasz return false; 29343a095abSBorawski.Lukasz } 294aecb47a4SBorawski.Lukasz 295900f9497SJoseph Reynolds return isOperationAllowedWithPrivileges(it->second, userPrivileges); 29686e1b661SBorawski.Lukasz } 297aecb47a4SBorawski.Lukasz 29886e1b661SBorawski.Lukasz } // namespace redfish 299