1*40e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0 2*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3*40e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation 486e1b661SBorawski.Lukasz #pragma once 586e1b661SBorawski.Lukasz 6d5c80ad9SNan Zhou #include "logging.hpp" 73e72c202SNinad Palsule #include "sessions.hpp" 8d5c80ad9SNan Zhou 9f00032dbSTanous #include <boost/beast/http/verb.hpp> 10aecb47a4SBorawski.Lukasz #include <boost/container/flat_map.hpp> 11d5c80ad9SNan Zhou #include <boost/container/vector.hpp> 121214b7e7SGunnar Mills 131214b7e7SGunnar Mills #include <array> 141214b7e7SGunnar Mills #include <bitset> 15d5c80ad9SNan Zhou #include <cstddef> 16d5c80ad9SNan Zhou #include <functional> 17d5c80ad9SNan Zhou #include <initializer_list> 18d5c80ad9SNan Zhou #include <string> 19d5c80ad9SNan Zhou #include <string_view> 20d5c80ad9SNan Zhou #include <utility> 211abe55efSEd Tanous #include <vector> 22aecb47a4SBorawski.Lukasz 231abe55efSEd Tanous namespace redfish 241abe55efSEd Tanous { 251abe55efSEd Tanous 261abe55efSEd Tanous enum class PrivilegeType 271abe55efSEd Tanous { 281abe55efSEd Tanous BASE, 291abe55efSEd Tanous OEM 301abe55efSEd Tanous }; 31aecb47a4SBorawski.Lukasz 32a692779fSEd Tanous /** @brief A fixed array of compile time privileges */ 33ac25adb8SEd Tanous constexpr std::array<std::string_view, 5> basePrivileges{ 343ebd75f7SEd Tanous "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", 353ebd75f7SEd Tanous "ConfigureUsers"}; 3643a095abSBorawski.Lukasz 37271584abSEd Tanous constexpr const size_t basePrivilegeCount = basePrivileges.size(); 38a692779fSEd Tanous 39a692779fSEd Tanous /** @brief Max number of privileges per type */ 40271584abSEd Tanous constexpr const size_t maxPrivilegeCount = 32; 41a692779fSEd Tanous 423e72c202SNinad Palsule /** 433e72c202SNinad Palsule * @brief A vector of all privilege names and their indexes 443e72c202SNinad Palsule * The privilege "OpenBMCHostConsole" is added to users who are members of the 453e72c202SNinad Palsule * "hostconsole" user group. This privilege is required to access the host 463e72c202SNinad Palsule * console. 473e72c202SNinad Palsule */ 48ac25adb8SEd Tanous constexpr std::array<std::string_view, maxPrivilegeCount> privilegeNames{ 493e72c202SNinad Palsule "Login", "ConfigureManager", "ConfigureComponents", 503e72c202SNinad Palsule "ConfigureSelf", "ConfigureUsers", "OpenBMCHostConsole"}; 5143a095abSBorawski.Lukasz 5286e1b661SBorawski.Lukasz /** 53aecb47a4SBorawski.Lukasz * @brief Redfish privileges 54aecb47a4SBorawski.Lukasz * 55900f9497SJoseph Reynolds * This implements a set of Redfish privileges. These directly represent 56900f9497SJoseph Reynolds * user privileges and help represent entity privileges. 57aecb47a4SBorawski.Lukasz * 5855c7b7a2SEd Tanous * Each incoming Connection requires a comparison between privileges held 59aecb47a4SBorawski.Lukasz * by the user issuing a request and the target entity's privileges. 60aecb47a4SBorawski.Lukasz * 61aecb47a4SBorawski.Lukasz * To ensure best runtime performance of this comparison, privileges 62aecb47a4SBorawski.Lukasz * are represented as bitsets. Each bit in the bitset corresponds to a 63aecb47a4SBorawski.Lukasz * unique privilege name. 64aecb47a4SBorawski.Lukasz * 65aecb47a4SBorawski.Lukasz * A bit is set if the privilege is required (entity domain) or granted 66aecb47a4SBorawski.Lukasz * (user domain) and false otherwise. 67aecb47a4SBorawski.Lukasz * 6886e1b661SBorawski.Lukasz */ 691abe55efSEd Tanous class Privileges 701abe55efSEd Tanous { 71aecb47a4SBorawski.Lukasz public: 72aecb47a4SBorawski.Lukasz /** 7343a095abSBorawski.Lukasz * @brief Constructs object without any privileges active 7443a095abSBorawski.Lukasz * 7543a095abSBorawski.Lukasz */ 7643a095abSBorawski.Lukasz Privileges() = default; 7743a095abSBorawski.Lukasz 7843a095abSBorawski.Lukasz /** 7943a095abSBorawski.Lukasz * @brief Constructs object with given privileges active 8043a095abSBorawski.Lukasz * 8143a095abSBorawski.Lukasz * @param[in] privilegeList List of privileges to be activated 8243a095abSBorawski.Lukasz * 8343a095abSBorawski.Lukasz */ 841abe55efSEd Tanous Privileges(std::initializer_list<const char*> privilegeList) 851abe55efSEd Tanous { 861abe55efSEd Tanous for (const char* privilege : privilegeList) 871abe55efSEd Tanous { 881abe55efSEd Tanous if (!setSinglePrivilege(privilege)) 891abe55efSEd Tanous { 9062598e31SEd Tanous BMCWEB_LOG_CRITICAL("Unable to set privilege {} in constructor", 9162598e31SEd Tanous privilege); 9243a095abSBorawski.Lukasz } 9343a095abSBorawski.Lukasz } 943ebd75f7SEd Tanous } 95aecb47a4SBorawski.Lukasz 96aecb47a4SBorawski.Lukasz /** 97aecb47a4SBorawski.Lukasz * @brief Sets given privilege in the bitset 98aecb47a4SBorawski.Lukasz * 99aecb47a4SBorawski.Lukasz * @param[in] privilege Privilege to be set 100aecb47a4SBorawski.Lukasz * 101aecb47a4SBorawski.Lukasz * @return None 10243a095abSBorawski.Lukasz * 103aecb47a4SBorawski.Lukasz */ 10426ccae32SEd Tanous bool setSinglePrivilege(std::string_view privilege) 1051abe55efSEd Tanous { 106271584abSEd Tanous for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 1071abe55efSEd Tanous searchIndex++) 1081abe55efSEd Tanous { 1091abe55efSEd Tanous if (privilege == privilegeNames[searchIndex]) 1101abe55efSEd Tanous { 11155c7b7a2SEd Tanous privilegeBitset.set(searchIndex); 1123ebd75f7SEd Tanous return true; 113aecb47a4SBorawski.Lukasz } 114a692779fSEd Tanous } 115aecb47a4SBorawski.Lukasz 1163ebd75f7SEd Tanous return false; 1173ebd75f7SEd Tanous } 1183ebd75f7SEd Tanous 1193ebd75f7SEd Tanous /** 120900f9497SJoseph Reynolds * @brief Resets the given privilege in the bitset 121900f9497SJoseph Reynolds * 122900f9497SJoseph Reynolds * @param[in] privilege Privilege to be reset 123900f9497SJoseph Reynolds * 124900f9497SJoseph Reynolds * @return None 125900f9497SJoseph Reynolds * 126900f9497SJoseph Reynolds */ 127900f9497SJoseph Reynolds bool resetSinglePrivilege(const char* privilege) 128900f9497SJoseph Reynolds { 129900f9497SJoseph Reynolds for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 130900f9497SJoseph Reynolds searchIndex++) 131900f9497SJoseph Reynolds { 132900f9497SJoseph Reynolds if (privilege == privilegeNames[searchIndex]) 133900f9497SJoseph Reynolds { 134900f9497SJoseph Reynolds privilegeBitset.reset(searchIndex); 135900f9497SJoseph Reynolds return true; 136900f9497SJoseph Reynolds } 137900f9497SJoseph Reynolds } 138900f9497SJoseph Reynolds return false; 139900f9497SJoseph Reynolds } 140900f9497SJoseph Reynolds 141900f9497SJoseph Reynolds /** 142aecb47a4SBorawski.Lukasz * @brief Retrieves names of all active privileges for a given type 143aecb47a4SBorawski.Lukasz * 144aecb47a4SBorawski.Lukasz * @param[in] type Base or OEM 145aecb47a4SBorawski.Lukasz * 1463ebd75f7SEd Tanous * @return Vector of active privileges. Pointers are valid until 147a692779fSEd Tanous * the setSinglePrivilege is called, or the Privilege structure is destroyed 14843a095abSBorawski.Lukasz * 149aecb47a4SBorawski.Lukasz */ 15023a21a1cSEd Tanous std::vector<std::string> 1511abe55efSEd Tanous getActivePrivilegeNames(const PrivilegeType type) const 1521abe55efSEd Tanous { 15323a21a1cSEd Tanous std::vector<std::string> activePrivileges; 154aecb47a4SBorawski.Lukasz 155271584abSEd Tanous size_t searchIndex = 0; 156271584abSEd Tanous size_t endIndex = basePrivilegeCount; 1571abe55efSEd Tanous if (type == PrivilegeType::OEM) 1581abe55efSEd Tanous { 15987704464SJoseph Reynolds searchIndex = basePrivilegeCount; 16055c7b7a2SEd Tanous endIndex = privilegeNames.size(); 161a692779fSEd Tanous } 162a692779fSEd Tanous 1631abe55efSEd Tanous for (; searchIndex < endIndex; searchIndex++) 1641abe55efSEd Tanous { 1651abe55efSEd Tanous if (privilegeBitset.test(searchIndex)) 1661abe55efSEd Tanous { 16723a21a1cSEd Tanous activePrivileges.emplace_back(privilegeNames[searchIndex]); 168aecb47a4SBorawski.Lukasz } 169aecb47a4SBorawski.Lukasz } 170a692779fSEd Tanous 171aecb47a4SBorawski.Lukasz return activePrivileges; 172aecb47a4SBorawski.Lukasz } 173aecb47a4SBorawski.Lukasz 1743ebd75f7SEd Tanous /** 1753ebd75f7SEd Tanous * @brief Determines if this Privilege set is a superset of the given 1763ebd75f7SEd Tanous * privilege set 1773ebd75f7SEd Tanous * 1783ebd75f7SEd Tanous * @param[in] privilege Privilege to be checked 1793ebd75f7SEd Tanous * 1803ebd75f7SEd Tanous * @return None 1813ebd75f7SEd Tanous * 1823ebd75f7SEd Tanous */ 1831abe55efSEd Tanous bool isSupersetOf(const Privileges& p) const 1841abe55efSEd Tanous { 185a692779fSEd Tanous return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset; 1863ebd75f7SEd Tanous } 1873ebd75f7SEd Tanous 1883bf4e632SJoseph Reynolds /** 1893bf4e632SJoseph Reynolds * @brief Returns the intersection of two Privilege sets. 1903bf4e632SJoseph Reynolds * 1913bf4e632SJoseph Reynolds * @param[in] privilege Privilege set to intersect with. 1923bf4e632SJoseph Reynolds * 1933bf4e632SJoseph Reynolds * @return The new Privilege set. 1943bf4e632SJoseph Reynolds * 1953bf4e632SJoseph Reynolds */ 1963bf4e632SJoseph Reynolds Privileges intersection(const Privileges& p) const 1973bf4e632SJoseph Reynolds { 1983bf4e632SJoseph Reynolds return Privileges{privilegeBitset & p.privilegeBitset}; 1993bf4e632SJoseph Reynolds } 2003bf4e632SJoseph Reynolds 20186e1b661SBorawski.Lukasz private: 2024e23a444SEd Tanous explicit Privileges(const std::bitset<maxPrivilegeCount>& p) : 2034e23a444SEd Tanous privilegeBitset{p} 2041214b7e7SGunnar Mills {} 20555c7b7a2SEd Tanous std::bitset<maxPrivilegeCount> privilegeBitset = 0; 20686e1b661SBorawski.Lukasz }; 20786e1b661SBorawski.Lukasz 2083e72c202SNinad Palsule inline Privileges getUserPrivileges(const persistent_data::UserSession& session) 2093e72c202SNinad Palsule { 2103e72c202SNinad Palsule // default to no access 2113e72c202SNinad Palsule Privileges privs; 2123e72c202SNinad Palsule 2133e72c202SNinad Palsule // Check if user is member of hostconsole group 2143e72c202SNinad Palsule for (const auto& userGroup : session.userGroups) 2153e72c202SNinad Palsule { 2163e72c202SNinad Palsule if (userGroup == "hostconsole") 2173e72c202SNinad Palsule { 2183e72c202SNinad Palsule // Redfish privilege : host console access 2193e72c202SNinad Palsule privs.setSinglePrivilege("OpenBMCHostConsole"); 2203e72c202SNinad Palsule break; 2213e72c202SNinad Palsule } 2223e72c202SNinad Palsule } 2233e72c202SNinad Palsule 2243e72c202SNinad Palsule if (session.userRole == "priv-admin") 2256f359568SRatan Gupta { 2266f359568SRatan Gupta // Redfish privilege : Administrator 2273e72c202SNinad Palsule privs.setSinglePrivilege("Login"); 2283e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureManager"); 2293e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureSelf"); 2303e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureUsers"); 2313e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureComponents"); 2326f359568SRatan Gupta } 2333e72c202SNinad Palsule else if (session.userRole == "priv-operator") 2346f359568SRatan Gupta { 2356f359568SRatan Gupta // Redfish privilege : Operator 2363e72c202SNinad Palsule privs.setSinglePrivilege("Login"); 2373e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureSelf"); 2383e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureComponents"); 2396f359568SRatan Gupta } 2403e72c202SNinad Palsule else if (session.userRole == "priv-user") 2416f359568SRatan Gupta { 2426f359568SRatan Gupta // Redfish privilege : Readonly 2433e72c202SNinad Palsule privs.setSinglePrivilege("Login"); 2443e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureSelf"); 2456f359568SRatan Gupta } 2463e72c202SNinad Palsule 2473e72c202SNinad Palsule return privs; 248d7e08029Sjayaprakash Mutyala } 2496f359568SRatan Gupta 250900f9497SJoseph Reynolds /** 251900f9497SJoseph Reynolds * @brief The OperationMap represents the privileges required for a 252900f9497SJoseph Reynolds * single entity (URI). It maps from the allowable verbs to the 253900f9497SJoseph Reynolds * privileges required to use that operation. 254900f9497SJoseph Reynolds * 255900f9497SJoseph Reynolds * This represents the Redfish "Privilege AND and OR syntax" as given 256900f9497SJoseph Reynolds * in the spec and shown in the Privilege Registry. This does not 257900f9497SJoseph Reynolds * implement any Redfish property overrides, subordinate overrides, or 258900f9497SJoseph Reynolds * resource URI overrides. This does not implement the limitation of 259900f9497SJoseph Reynolds * the ConfigureSelf privilege to operate only on your own account or 260900f9497SJoseph Reynolds * session. 261900f9497SJoseph Reynolds **/ 262e0d918bcSEd Tanous using OperationMap = boost::container::flat_map<boost::beast::http::verb, 263e0d918bcSEd Tanous std::vector<Privileges>>; 26443a095abSBorawski.Lukasz 265900f9497SJoseph Reynolds /* @brief Checks if user is allowed to call an operation 266900f9497SJoseph Reynolds * 267900f9497SJoseph Reynolds * @param[in] operationPrivilegesRequired Privileges required 268900f9497SJoseph Reynolds * @param[in] userPrivileges Privileges the user has 269900f9497SJoseph Reynolds * 270900f9497SJoseph Reynolds * @return True if operation is allowed, false otherwise 271900f9497SJoseph Reynolds */ 272900f9497SJoseph Reynolds inline bool isOperationAllowedWithPrivileges( 273900f9497SJoseph Reynolds const std::vector<Privileges>& operationPrivilegesRequired, 274900f9497SJoseph Reynolds const Privileges& userPrivileges) 275900f9497SJoseph Reynolds { 276900f9497SJoseph Reynolds // If there are no privileges assigned, there are no privileges required 277900f9497SJoseph Reynolds if (operationPrivilegesRequired.empty()) 278900f9497SJoseph Reynolds { 279900f9497SJoseph Reynolds return true; 280900f9497SJoseph Reynolds } 2819eb808c1SEd Tanous for (const auto& requiredPrivileges : operationPrivilegesRequired) 282900f9497SJoseph Reynolds { 28362598e31SEd Tanous BMCWEB_LOG_DEBUG("Checking operation privileges..."); 284900f9497SJoseph Reynolds if (userPrivileges.isSupersetOf(requiredPrivileges)) 285900f9497SJoseph Reynolds { 28662598e31SEd Tanous BMCWEB_LOG_DEBUG("...success"); 287900f9497SJoseph Reynolds return true; 288900f9497SJoseph Reynolds } 289900f9497SJoseph Reynolds } 290900f9497SJoseph Reynolds return false; 291900f9497SJoseph Reynolds } 292900f9497SJoseph Reynolds 29343a095abSBorawski.Lukasz /** 294aecb47a4SBorawski.Lukasz * @brief Checks if given privileges allow to call an HTTP method 295aecb47a4SBorawski.Lukasz * 296aecb47a4SBorawski.Lukasz * @param[in] method HTTP method 297aecb47a4SBorawski.Lukasz * @param[in] user Privileges 298aecb47a4SBorawski.Lukasz * 299aecb47a4SBorawski.Lukasz * @return True if method allowed, false otherwise 30043a095abSBorawski.Lukasz * 301aecb47a4SBorawski.Lukasz */ 302e0d918bcSEd Tanous inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, 3033ebd75f7SEd Tanous const OperationMap& operationMap, 3041abe55efSEd Tanous const Privileges& userPrivileges) 3051abe55efSEd Tanous { 3063ebd75f7SEd Tanous const auto& it = operationMap.find(method); 3071abe55efSEd Tanous if (it == operationMap.end()) 3081abe55efSEd Tanous { 30943a095abSBorawski.Lukasz return false; 31043a095abSBorawski.Lukasz } 311aecb47a4SBorawski.Lukasz 312900f9497SJoseph Reynolds return isOperationAllowedWithPrivileges(it->second, userPrivileges); 31386e1b661SBorawski.Lukasz } 314aecb47a4SBorawski.Lukasz 31586e1b661SBorawski.Lukasz } // namespace redfish 316