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 18*d5c80ad9SNan Zhou #include "logging.hpp" 19*d5c80ad9SNan Zhou 20f00032dbSTanous #include <boost/beast/http/verb.hpp> 21aecb47a4SBorawski.Lukasz #include <boost/container/flat_map.hpp> 22*d5c80ad9SNan Zhou #include <boost/container/vector.hpp> 23*d5c80ad9SNan Zhou #include <boost/move/algo/move.hpp> 241214b7e7SGunnar Mills 251214b7e7SGunnar Mills #include <array> 261214b7e7SGunnar Mills #include <bitset> 27*d5c80ad9SNan Zhou #include <cstddef> 28*d5c80ad9SNan Zhou #include <functional> 29*d5c80ad9SNan Zhou #include <initializer_list> 30*d5c80ad9SNan Zhou #include <string> 31*d5c80ad9SNan Zhou #include <string_view> 32*d5c80ad9SNan Zhou #include <utility> 331abe55efSEd Tanous #include <vector> 34aecb47a4SBorawski.Lukasz 35*d5c80ad9SNan Zhou // IWYU pragma: no_include <stddef.h> 36*d5c80ad9SNan Zhou 371abe55efSEd Tanous namespace redfish 381abe55efSEd Tanous { 391abe55efSEd Tanous 401abe55efSEd Tanous enum class PrivilegeType 411abe55efSEd Tanous { 421abe55efSEd Tanous BASE, 431abe55efSEd Tanous OEM 441abe55efSEd Tanous }; 45aecb47a4SBorawski.Lukasz 46a692779fSEd Tanous /** @brief A fixed array of compile time privileges */ 47a692779fSEd Tanous constexpr std::array<const char*, 5> basePrivileges{ 483ebd75f7SEd Tanous "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", 493ebd75f7SEd Tanous "ConfigureUsers"}; 5043a095abSBorawski.Lukasz 51271584abSEd Tanous constexpr const size_t basePrivilegeCount = basePrivileges.size(); 52a692779fSEd Tanous 53a692779fSEd Tanous /** @brief Max number of privileges per type */ 54271584abSEd Tanous constexpr const size_t maxPrivilegeCount = 32; 55a692779fSEd Tanous 56a692779fSEd Tanous /** @brief A vector of all privilege names and their indexes */ 5723a21a1cSEd Tanous static const std::array<std::string, maxPrivilegeCount> privilegeNames{ 5823a21a1cSEd Tanous "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", 5923a21a1cSEd Tanous "ConfigureUsers"}; 6043a095abSBorawski.Lukasz 6186e1b661SBorawski.Lukasz /** 62aecb47a4SBorawski.Lukasz * @brief Redfish privileges 63aecb47a4SBorawski.Lukasz * 64900f9497SJoseph Reynolds * This implements a set of Redfish privileges. These directly represent 65900f9497SJoseph Reynolds * user privileges and help represent entity privileges. 66aecb47a4SBorawski.Lukasz * 6755c7b7a2SEd Tanous * Each incoming Connection requires a comparison between privileges held 68aecb47a4SBorawski.Lukasz * by the user issuing a request and the target entity's privileges. 69aecb47a4SBorawski.Lukasz * 70aecb47a4SBorawski.Lukasz * To ensure best runtime performance of this comparison, privileges 71aecb47a4SBorawski.Lukasz * are represented as bitsets. Each bit in the bitset corresponds to a 72aecb47a4SBorawski.Lukasz * unique privilege name. 73aecb47a4SBorawski.Lukasz * 74aecb47a4SBorawski.Lukasz * A bit is set if the privilege is required (entity domain) or granted 75aecb47a4SBorawski.Lukasz * (user domain) and false otherwise. 76aecb47a4SBorawski.Lukasz * 7786e1b661SBorawski.Lukasz */ 781abe55efSEd Tanous class Privileges 791abe55efSEd Tanous { 80aecb47a4SBorawski.Lukasz public: 81aecb47a4SBorawski.Lukasz /** 8243a095abSBorawski.Lukasz * @brief Constructs object without any privileges active 8343a095abSBorawski.Lukasz * 8443a095abSBorawski.Lukasz */ 8543a095abSBorawski.Lukasz Privileges() = default; 8643a095abSBorawski.Lukasz 8743a095abSBorawski.Lukasz /** 8843a095abSBorawski.Lukasz * @brief Constructs object with given privileges active 8943a095abSBorawski.Lukasz * 9043a095abSBorawski.Lukasz * @param[in] privilegeList List of privileges to be activated 9143a095abSBorawski.Lukasz * 9243a095abSBorawski.Lukasz */ 931abe55efSEd Tanous Privileges(std::initializer_list<const char*> privilegeList) 941abe55efSEd Tanous { 951abe55efSEd Tanous for (const char* privilege : privilegeList) 961abe55efSEd Tanous { 971abe55efSEd Tanous if (!setSinglePrivilege(privilege)) 981abe55efSEd Tanous { 9955c7b7a2SEd Tanous BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege 1003ebd75f7SEd Tanous << "in constructor"; 10143a095abSBorawski.Lukasz } 10243a095abSBorawski.Lukasz } 1033ebd75f7SEd Tanous } 104aecb47a4SBorawski.Lukasz 105aecb47a4SBorawski.Lukasz /** 106aecb47a4SBorawski.Lukasz * @brief Sets given privilege in the bitset 107aecb47a4SBorawski.Lukasz * 108aecb47a4SBorawski.Lukasz * @param[in] privilege Privilege to be set 109aecb47a4SBorawski.Lukasz * 110aecb47a4SBorawski.Lukasz * @return None 11143a095abSBorawski.Lukasz * 112aecb47a4SBorawski.Lukasz */ 11323a21a1cSEd Tanous bool setSinglePrivilege(const std::string_view privilege) 1141abe55efSEd Tanous { 115271584abSEd Tanous for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 1161abe55efSEd Tanous searchIndex++) 1171abe55efSEd Tanous { 1181abe55efSEd Tanous if (privilege == privilegeNames[searchIndex]) 1191abe55efSEd Tanous { 12055c7b7a2SEd Tanous privilegeBitset.set(searchIndex); 1213ebd75f7SEd Tanous return true; 122aecb47a4SBorawski.Lukasz } 123a692779fSEd Tanous } 124aecb47a4SBorawski.Lukasz 1253ebd75f7SEd Tanous return false; 1263ebd75f7SEd Tanous } 1273ebd75f7SEd Tanous 1283ebd75f7SEd Tanous /** 129900f9497SJoseph Reynolds * @brief Resets the given privilege in the bitset 130900f9497SJoseph Reynolds * 131900f9497SJoseph Reynolds * @param[in] privilege Privilege to be reset 132900f9497SJoseph Reynolds * 133900f9497SJoseph Reynolds * @return None 134900f9497SJoseph Reynolds * 135900f9497SJoseph Reynolds */ 136900f9497SJoseph Reynolds bool resetSinglePrivilege(const char* privilege) 137900f9497SJoseph Reynolds { 138900f9497SJoseph Reynolds for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 139900f9497SJoseph Reynolds searchIndex++) 140900f9497SJoseph Reynolds { 141900f9497SJoseph Reynolds if (privilege == privilegeNames[searchIndex]) 142900f9497SJoseph Reynolds { 143900f9497SJoseph Reynolds privilegeBitset.reset(searchIndex); 144900f9497SJoseph Reynolds return true; 145900f9497SJoseph Reynolds } 146900f9497SJoseph Reynolds } 147900f9497SJoseph Reynolds return false; 148900f9497SJoseph Reynolds } 149900f9497SJoseph Reynolds 150900f9497SJoseph Reynolds /** 151aecb47a4SBorawski.Lukasz * @brief Retrieves names of all active privileges for a given type 152aecb47a4SBorawski.Lukasz * 153aecb47a4SBorawski.Lukasz * @param[in] type Base or OEM 154aecb47a4SBorawski.Lukasz * 1553ebd75f7SEd Tanous * @return Vector of active privileges. Pointers are valid until 156a692779fSEd Tanous * the setSinglePrivilege is called, or the Privilege structure is destroyed 15743a095abSBorawski.Lukasz * 158aecb47a4SBorawski.Lukasz */ 15923a21a1cSEd Tanous std::vector<std::string> 1601abe55efSEd Tanous getActivePrivilegeNames(const PrivilegeType type) const 1611abe55efSEd Tanous { 16223a21a1cSEd Tanous std::vector<std::string> activePrivileges; 163aecb47a4SBorawski.Lukasz 164271584abSEd Tanous size_t searchIndex = 0; 165271584abSEd Tanous size_t endIndex = basePrivilegeCount; 1661abe55efSEd Tanous if (type == PrivilegeType::OEM) 1671abe55efSEd Tanous { 16887704464SJoseph Reynolds searchIndex = basePrivilegeCount; 16955c7b7a2SEd Tanous endIndex = privilegeNames.size(); 170a692779fSEd Tanous } 171a692779fSEd Tanous 1721abe55efSEd Tanous for (; searchIndex < endIndex; searchIndex++) 1731abe55efSEd Tanous { 1741abe55efSEd Tanous if (privilegeBitset.test(searchIndex)) 1751abe55efSEd Tanous { 17623a21a1cSEd Tanous activePrivileges.emplace_back(privilegeNames[searchIndex]); 177aecb47a4SBorawski.Lukasz } 178aecb47a4SBorawski.Lukasz } 179a692779fSEd Tanous 180aecb47a4SBorawski.Lukasz return activePrivileges; 181aecb47a4SBorawski.Lukasz } 182aecb47a4SBorawski.Lukasz 1833ebd75f7SEd Tanous /** 1843ebd75f7SEd Tanous * @brief Determines if this Privilege set is a superset of the given 1853ebd75f7SEd Tanous * privilege set 1863ebd75f7SEd Tanous * 1873ebd75f7SEd Tanous * @param[in] privilege Privilege to be checked 1883ebd75f7SEd Tanous * 1893ebd75f7SEd Tanous * @return None 1903ebd75f7SEd Tanous * 1913ebd75f7SEd Tanous */ 1921abe55efSEd Tanous bool isSupersetOf(const Privileges& p) const 1931abe55efSEd Tanous { 194a692779fSEd Tanous return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset; 1953ebd75f7SEd Tanous } 1963ebd75f7SEd Tanous 1973bf4e632SJoseph Reynolds /** 1983bf4e632SJoseph Reynolds * @brief Returns the intersection of two Privilege sets. 1993bf4e632SJoseph Reynolds * 2003bf4e632SJoseph Reynolds * @param[in] privilege Privilege set to intersect with. 2013bf4e632SJoseph Reynolds * 2023bf4e632SJoseph Reynolds * @return The new Privilege set. 2033bf4e632SJoseph Reynolds * 2043bf4e632SJoseph Reynolds */ 2053bf4e632SJoseph Reynolds Privileges intersection(const Privileges& p) const 2063bf4e632SJoseph Reynolds { 2073bf4e632SJoseph Reynolds return Privileges{privilegeBitset & p.privilegeBitset}; 2083bf4e632SJoseph Reynolds } 2093bf4e632SJoseph Reynolds 21086e1b661SBorawski.Lukasz private: 2114e23a444SEd Tanous explicit Privileges(const std::bitset<maxPrivilegeCount>& p) : 2124e23a444SEd Tanous privilegeBitset{p} 2131214b7e7SGunnar Mills {} 21455c7b7a2SEd Tanous std::bitset<maxPrivilegeCount> privilegeBitset = 0; 21586e1b661SBorawski.Lukasz }; 21686e1b661SBorawski.Lukasz 2176f359568SRatan Gupta inline const Privileges& getUserPrivileges(const std::string& userRole) 2186f359568SRatan Gupta { 2196f359568SRatan Gupta // Redfish privilege : Administrator 2206f359568SRatan Gupta if (userRole == "priv-admin") 2216f359568SRatan Gupta { 2226f359568SRatan Gupta static Privileges admin{"Login", "ConfigureManager", "ConfigureSelf", 2236f359568SRatan Gupta "ConfigureUsers", "ConfigureComponents"}; 2246f359568SRatan Gupta return admin; 2256f359568SRatan Gupta } 2263174e4dfSEd Tanous if (userRole == "priv-operator") 2276f359568SRatan Gupta { 2286f359568SRatan Gupta // Redfish privilege : Operator 2296f359568SRatan Gupta static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"}; 2306f359568SRatan Gupta return op; 2316f359568SRatan Gupta } 2323174e4dfSEd Tanous if (userRole == "priv-user") 2336f359568SRatan Gupta { 2346f359568SRatan Gupta // Redfish privilege : Readonly 2356f359568SRatan Gupta static Privileges readOnly{"Login", "ConfigureSelf"}; 2366f359568SRatan Gupta return readOnly; 2376f359568SRatan Gupta } 238d7e08029Sjayaprakash Mutyala // Redfish privilege : NoAccess 239d7e08029Sjayaprakash Mutyala static Privileges noaccess; 240d7e08029Sjayaprakash Mutyala return noaccess; 241d7e08029Sjayaprakash Mutyala } 2426f359568SRatan Gupta 243900f9497SJoseph Reynolds /** 244900f9497SJoseph Reynolds * @brief The OperationMap represents the privileges required for a 245900f9497SJoseph Reynolds * single entity (URI). It maps from the allowable verbs to the 246900f9497SJoseph Reynolds * privileges required to use that operation. 247900f9497SJoseph Reynolds * 248900f9497SJoseph Reynolds * This represents the Redfish "Privilege AND and OR syntax" as given 249900f9497SJoseph Reynolds * in the spec and shown in the Privilege Registry. This does not 250900f9497SJoseph Reynolds * implement any Redfish property overrides, subordinate overrides, or 251900f9497SJoseph Reynolds * resource URI overrides. This does not implement the limitation of 252900f9497SJoseph Reynolds * the ConfigureSelf privilege to operate only on your own account or 253900f9497SJoseph Reynolds * session. 254900f9497SJoseph Reynolds **/ 255e0d918bcSEd Tanous using OperationMap = boost::container::flat_map<boost::beast::http::verb, 256e0d918bcSEd Tanous std::vector<Privileges>>; 25743a095abSBorawski.Lukasz 258900f9497SJoseph Reynolds /* @brief Checks if user is allowed to call an operation 259900f9497SJoseph Reynolds * 260900f9497SJoseph Reynolds * @param[in] operationPrivilegesRequired Privileges required 261900f9497SJoseph Reynolds * @param[in] userPrivileges Privileges the user has 262900f9497SJoseph Reynolds * 263900f9497SJoseph Reynolds * @return True if operation is allowed, false otherwise 264900f9497SJoseph Reynolds */ 265900f9497SJoseph Reynolds inline bool isOperationAllowedWithPrivileges( 266900f9497SJoseph Reynolds const std::vector<Privileges>& operationPrivilegesRequired, 267900f9497SJoseph Reynolds const Privileges& userPrivileges) 268900f9497SJoseph Reynolds { 269900f9497SJoseph Reynolds // If there are no privileges assigned, there are no privileges required 270900f9497SJoseph Reynolds if (operationPrivilegesRequired.empty()) 271900f9497SJoseph Reynolds { 272900f9497SJoseph Reynolds return true; 273900f9497SJoseph Reynolds } 2749eb808c1SEd Tanous for (const auto& requiredPrivileges : operationPrivilegesRequired) 275900f9497SJoseph Reynolds { 27654fbf177SAndrew Geissler BMCWEB_LOG_DEBUG << "Checking operation privileges..."; 277900f9497SJoseph Reynolds if (userPrivileges.isSupersetOf(requiredPrivileges)) 278900f9497SJoseph Reynolds { 27954fbf177SAndrew Geissler BMCWEB_LOG_DEBUG << "...success"; 280900f9497SJoseph Reynolds return true; 281900f9497SJoseph Reynolds } 282900f9497SJoseph Reynolds } 283900f9497SJoseph Reynolds return false; 284900f9497SJoseph Reynolds } 285900f9497SJoseph Reynolds 28643a095abSBorawski.Lukasz /** 287aecb47a4SBorawski.Lukasz * @brief Checks if given privileges allow to call an HTTP method 288aecb47a4SBorawski.Lukasz * 289aecb47a4SBorawski.Lukasz * @param[in] method HTTP method 290aecb47a4SBorawski.Lukasz * @param[in] user Privileges 291aecb47a4SBorawski.Lukasz * 292aecb47a4SBorawski.Lukasz * @return True if method allowed, false otherwise 29343a095abSBorawski.Lukasz * 294aecb47a4SBorawski.Lukasz */ 295e0d918bcSEd Tanous inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, 2963ebd75f7SEd Tanous const OperationMap& operationMap, 2971abe55efSEd Tanous const Privileges& userPrivileges) 2981abe55efSEd Tanous { 2993ebd75f7SEd Tanous const auto& it = operationMap.find(method); 3001abe55efSEd Tanous if (it == operationMap.end()) 3011abe55efSEd Tanous { 30243a095abSBorawski.Lukasz return false; 30343a095abSBorawski.Lukasz } 304aecb47a4SBorawski.Lukasz 305900f9497SJoseph Reynolds return isOperationAllowedWithPrivileges(it->second, userPrivileges); 30686e1b661SBorawski.Lukasz } 307aecb47a4SBorawski.Lukasz 30886e1b661SBorawski.Lukasz } // namespace redfish 309