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 18d5c80ad9SNan Zhou #include "logging.hpp" 19*3e72c202SNinad Palsule #include "sessions.hpp" 20d5c80ad9SNan Zhou 21f00032dbSTanous #include <boost/beast/http/verb.hpp> 22aecb47a4SBorawski.Lukasz #include <boost/container/flat_map.hpp> 23d5c80ad9SNan Zhou #include <boost/container/vector.hpp> 24d5c80ad9SNan Zhou #include <boost/move/algo/move.hpp> 251214b7e7SGunnar Mills 261214b7e7SGunnar Mills #include <array> 271214b7e7SGunnar Mills #include <bitset> 28d5c80ad9SNan Zhou #include <cstddef> 29d5c80ad9SNan Zhou #include <functional> 30d5c80ad9SNan Zhou #include <initializer_list> 31d5c80ad9SNan Zhou #include <string> 32d5c80ad9SNan Zhou #include <string_view> 33d5c80ad9SNan Zhou #include <utility> 341abe55efSEd Tanous #include <vector> 35aecb47a4SBorawski.Lukasz 36d5c80ad9SNan Zhou // IWYU pragma: no_include <stddef.h> 37d5c80ad9SNan Zhou 381abe55efSEd Tanous namespace redfish 391abe55efSEd Tanous { 401abe55efSEd Tanous 411abe55efSEd Tanous enum class PrivilegeType 421abe55efSEd Tanous { 431abe55efSEd Tanous BASE, 441abe55efSEd Tanous OEM 451abe55efSEd Tanous }; 46aecb47a4SBorawski.Lukasz 47a692779fSEd Tanous /** @brief A fixed array of compile time privileges */ 48a692779fSEd Tanous constexpr std::array<const char*, 5> basePrivileges{ 493ebd75f7SEd Tanous "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", 503ebd75f7SEd Tanous "ConfigureUsers"}; 5143a095abSBorawski.Lukasz 52271584abSEd Tanous constexpr const size_t basePrivilegeCount = basePrivileges.size(); 53a692779fSEd Tanous 54a692779fSEd Tanous /** @brief Max number of privileges per type */ 55271584abSEd Tanous constexpr const size_t maxPrivilegeCount = 32; 56a692779fSEd Tanous 57*3e72c202SNinad Palsule /** 58*3e72c202SNinad Palsule * @brief A vector of all privilege names and their indexes 59*3e72c202SNinad Palsule * The privilege "OpenBMCHostConsole" is added to users who are members of the 60*3e72c202SNinad Palsule * "hostconsole" user group. This privilege is required to access the host 61*3e72c202SNinad Palsule * console. 62*3e72c202SNinad Palsule */ 6323a21a1cSEd Tanous static const std::array<std::string, maxPrivilegeCount> privilegeNames{ 64*3e72c202SNinad Palsule "Login", "ConfigureManager", "ConfigureComponents", 65*3e72c202SNinad Palsule "ConfigureSelf", "ConfigureUsers", "OpenBMCHostConsole"}; 6643a095abSBorawski.Lukasz 6786e1b661SBorawski.Lukasz /** 68aecb47a4SBorawski.Lukasz * @brief Redfish privileges 69aecb47a4SBorawski.Lukasz * 70900f9497SJoseph Reynolds * This implements a set of Redfish privileges. These directly represent 71900f9497SJoseph Reynolds * user privileges and help represent entity privileges. 72aecb47a4SBorawski.Lukasz * 7355c7b7a2SEd Tanous * Each incoming Connection requires a comparison between privileges held 74aecb47a4SBorawski.Lukasz * by the user issuing a request and the target entity's privileges. 75aecb47a4SBorawski.Lukasz * 76aecb47a4SBorawski.Lukasz * To ensure best runtime performance of this comparison, privileges 77aecb47a4SBorawski.Lukasz * are represented as bitsets. Each bit in the bitset corresponds to a 78aecb47a4SBorawski.Lukasz * unique privilege name. 79aecb47a4SBorawski.Lukasz * 80aecb47a4SBorawski.Lukasz * A bit is set if the privilege is required (entity domain) or granted 81aecb47a4SBorawski.Lukasz * (user domain) and false otherwise. 82aecb47a4SBorawski.Lukasz * 8386e1b661SBorawski.Lukasz */ 841abe55efSEd Tanous class Privileges 851abe55efSEd Tanous { 86aecb47a4SBorawski.Lukasz public: 87aecb47a4SBorawski.Lukasz /** 8843a095abSBorawski.Lukasz * @brief Constructs object without any privileges active 8943a095abSBorawski.Lukasz * 9043a095abSBorawski.Lukasz */ 9143a095abSBorawski.Lukasz Privileges() = default; 9243a095abSBorawski.Lukasz 9343a095abSBorawski.Lukasz /** 9443a095abSBorawski.Lukasz * @brief Constructs object with given privileges active 9543a095abSBorawski.Lukasz * 9643a095abSBorawski.Lukasz * @param[in] privilegeList List of privileges to be activated 9743a095abSBorawski.Lukasz * 9843a095abSBorawski.Lukasz */ 991abe55efSEd Tanous Privileges(std::initializer_list<const char*> privilegeList) 1001abe55efSEd Tanous { 1011abe55efSEd Tanous for (const char* privilege : privilegeList) 1021abe55efSEd Tanous { 1031abe55efSEd Tanous if (!setSinglePrivilege(privilege)) 1041abe55efSEd Tanous { 10555c7b7a2SEd Tanous BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege 1063ebd75f7SEd Tanous << "in constructor"; 10743a095abSBorawski.Lukasz } 10843a095abSBorawski.Lukasz } 1093ebd75f7SEd Tanous } 110aecb47a4SBorawski.Lukasz 111aecb47a4SBorawski.Lukasz /** 112aecb47a4SBorawski.Lukasz * @brief Sets given privilege in the bitset 113aecb47a4SBorawski.Lukasz * 114aecb47a4SBorawski.Lukasz * @param[in] privilege Privilege to be set 115aecb47a4SBorawski.Lukasz * 116aecb47a4SBorawski.Lukasz * @return None 11743a095abSBorawski.Lukasz * 118aecb47a4SBorawski.Lukasz */ 11926ccae32SEd Tanous bool setSinglePrivilege(std::string_view privilege) 1201abe55efSEd Tanous { 121271584abSEd Tanous for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 1221abe55efSEd Tanous searchIndex++) 1231abe55efSEd Tanous { 1241abe55efSEd Tanous if (privilege == privilegeNames[searchIndex]) 1251abe55efSEd Tanous { 12655c7b7a2SEd Tanous privilegeBitset.set(searchIndex); 1273ebd75f7SEd Tanous return true; 128aecb47a4SBorawski.Lukasz } 129a692779fSEd Tanous } 130aecb47a4SBorawski.Lukasz 1313ebd75f7SEd Tanous return false; 1323ebd75f7SEd Tanous } 1333ebd75f7SEd Tanous 1343ebd75f7SEd Tanous /** 135900f9497SJoseph Reynolds * @brief Resets the given privilege in the bitset 136900f9497SJoseph Reynolds * 137900f9497SJoseph Reynolds * @param[in] privilege Privilege to be reset 138900f9497SJoseph Reynolds * 139900f9497SJoseph Reynolds * @return None 140900f9497SJoseph Reynolds * 141900f9497SJoseph Reynolds */ 142900f9497SJoseph Reynolds bool resetSinglePrivilege(const char* privilege) 143900f9497SJoseph Reynolds { 144900f9497SJoseph Reynolds for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 145900f9497SJoseph Reynolds searchIndex++) 146900f9497SJoseph Reynolds { 147900f9497SJoseph Reynolds if (privilege == privilegeNames[searchIndex]) 148900f9497SJoseph Reynolds { 149900f9497SJoseph Reynolds privilegeBitset.reset(searchIndex); 150900f9497SJoseph Reynolds return true; 151900f9497SJoseph Reynolds } 152900f9497SJoseph Reynolds } 153900f9497SJoseph Reynolds return false; 154900f9497SJoseph Reynolds } 155900f9497SJoseph Reynolds 156900f9497SJoseph Reynolds /** 157aecb47a4SBorawski.Lukasz * @brief Retrieves names of all active privileges for a given type 158aecb47a4SBorawski.Lukasz * 159aecb47a4SBorawski.Lukasz * @param[in] type Base or OEM 160aecb47a4SBorawski.Lukasz * 1613ebd75f7SEd Tanous * @return Vector of active privileges. Pointers are valid until 162a692779fSEd Tanous * the setSinglePrivilege is called, or the Privilege structure is destroyed 16343a095abSBorawski.Lukasz * 164aecb47a4SBorawski.Lukasz */ 16523a21a1cSEd Tanous std::vector<std::string> 1661abe55efSEd Tanous getActivePrivilegeNames(const PrivilegeType type) const 1671abe55efSEd Tanous { 16823a21a1cSEd Tanous std::vector<std::string> activePrivileges; 169aecb47a4SBorawski.Lukasz 170271584abSEd Tanous size_t searchIndex = 0; 171271584abSEd Tanous size_t endIndex = basePrivilegeCount; 1721abe55efSEd Tanous if (type == PrivilegeType::OEM) 1731abe55efSEd Tanous { 17487704464SJoseph Reynolds searchIndex = basePrivilegeCount; 17555c7b7a2SEd Tanous endIndex = privilegeNames.size(); 176a692779fSEd Tanous } 177a692779fSEd Tanous 1781abe55efSEd Tanous for (; searchIndex < endIndex; searchIndex++) 1791abe55efSEd Tanous { 1801abe55efSEd Tanous if (privilegeBitset.test(searchIndex)) 1811abe55efSEd Tanous { 18223a21a1cSEd Tanous activePrivileges.emplace_back(privilegeNames[searchIndex]); 183aecb47a4SBorawski.Lukasz } 184aecb47a4SBorawski.Lukasz } 185a692779fSEd Tanous 186aecb47a4SBorawski.Lukasz return activePrivileges; 187aecb47a4SBorawski.Lukasz } 188aecb47a4SBorawski.Lukasz 1893ebd75f7SEd Tanous /** 1903ebd75f7SEd Tanous * @brief Determines if this Privilege set is a superset of the given 1913ebd75f7SEd Tanous * privilege set 1923ebd75f7SEd Tanous * 1933ebd75f7SEd Tanous * @param[in] privilege Privilege to be checked 1943ebd75f7SEd Tanous * 1953ebd75f7SEd Tanous * @return None 1963ebd75f7SEd Tanous * 1973ebd75f7SEd Tanous */ 1981abe55efSEd Tanous bool isSupersetOf(const Privileges& p) const 1991abe55efSEd Tanous { 200a692779fSEd Tanous return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset; 2013ebd75f7SEd Tanous } 2023ebd75f7SEd Tanous 2033bf4e632SJoseph Reynolds /** 2043bf4e632SJoseph Reynolds * @brief Returns the intersection of two Privilege sets. 2053bf4e632SJoseph Reynolds * 2063bf4e632SJoseph Reynolds * @param[in] privilege Privilege set to intersect with. 2073bf4e632SJoseph Reynolds * 2083bf4e632SJoseph Reynolds * @return The new Privilege set. 2093bf4e632SJoseph Reynolds * 2103bf4e632SJoseph Reynolds */ 2113bf4e632SJoseph Reynolds Privileges intersection(const Privileges& p) const 2123bf4e632SJoseph Reynolds { 2133bf4e632SJoseph Reynolds return Privileges{privilegeBitset & p.privilegeBitset}; 2143bf4e632SJoseph Reynolds } 2153bf4e632SJoseph Reynolds 21686e1b661SBorawski.Lukasz private: 2174e23a444SEd Tanous explicit Privileges(const std::bitset<maxPrivilegeCount>& p) : 2184e23a444SEd Tanous privilegeBitset{p} 2191214b7e7SGunnar Mills {} 22055c7b7a2SEd Tanous std::bitset<maxPrivilegeCount> privilegeBitset = 0; 22186e1b661SBorawski.Lukasz }; 22286e1b661SBorawski.Lukasz 223*3e72c202SNinad Palsule inline Privileges getUserPrivileges(const persistent_data::UserSession& session) 224*3e72c202SNinad Palsule { 225*3e72c202SNinad Palsule // default to no access 226*3e72c202SNinad Palsule Privileges privs; 227*3e72c202SNinad Palsule 228*3e72c202SNinad Palsule // Check if user is member of hostconsole group 229*3e72c202SNinad Palsule for (const auto& userGroup : session.userGroups) 230*3e72c202SNinad Palsule { 231*3e72c202SNinad Palsule if (userGroup == "hostconsole") 232*3e72c202SNinad Palsule { 233*3e72c202SNinad Palsule // Redfish privilege : host console access 234*3e72c202SNinad Palsule privs.setSinglePrivilege("OpenBMCHostConsole"); 235*3e72c202SNinad Palsule break; 236*3e72c202SNinad Palsule } 237*3e72c202SNinad Palsule } 238*3e72c202SNinad Palsule 239*3e72c202SNinad Palsule if (session.userRole == "priv-admin") 2406f359568SRatan Gupta { 2416f359568SRatan Gupta // Redfish privilege : Administrator 242*3e72c202SNinad Palsule privs.setSinglePrivilege("Login"); 243*3e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureManager"); 244*3e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureSelf"); 245*3e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureUsers"); 246*3e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureComponents"); 2476f359568SRatan Gupta } 248*3e72c202SNinad Palsule else if (session.userRole == "priv-operator") 2496f359568SRatan Gupta { 2506f359568SRatan Gupta // Redfish privilege : Operator 251*3e72c202SNinad Palsule privs.setSinglePrivilege("Login"); 252*3e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureSelf"); 253*3e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureComponents"); 2546f359568SRatan Gupta } 255*3e72c202SNinad Palsule else if (session.userRole == "priv-user") 2566f359568SRatan Gupta { 2576f359568SRatan Gupta // Redfish privilege : Readonly 258*3e72c202SNinad Palsule privs.setSinglePrivilege("Login"); 259*3e72c202SNinad Palsule privs.setSinglePrivilege("ConfigureSelf"); 2606f359568SRatan Gupta } 261*3e72c202SNinad Palsule 262*3e72c202SNinad Palsule return privs; 263d7e08029Sjayaprakash Mutyala } 2646f359568SRatan Gupta 265900f9497SJoseph Reynolds /** 266900f9497SJoseph Reynolds * @brief The OperationMap represents the privileges required for a 267900f9497SJoseph Reynolds * single entity (URI). It maps from the allowable verbs to the 268900f9497SJoseph Reynolds * privileges required to use that operation. 269900f9497SJoseph Reynolds * 270900f9497SJoseph Reynolds * This represents the Redfish "Privilege AND and OR syntax" as given 271900f9497SJoseph Reynolds * in the spec and shown in the Privilege Registry. This does not 272900f9497SJoseph Reynolds * implement any Redfish property overrides, subordinate overrides, or 273900f9497SJoseph Reynolds * resource URI overrides. This does not implement the limitation of 274900f9497SJoseph Reynolds * the ConfigureSelf privilege to operate only on your own account or 275900f9497SJoseph Reynolds * session. 276900f9497SJoseph Reynolds **/ 277e0d918bcSEd Tanous using OperationMap = boost::container::flat_map<boost::beast::http::verb, 278e0d918bcSEd Tanous std::vector<Privileges>>; 27943a095abSBorawski.Lukasz 280900f9497SJoseph Reynolds /* @brief Checks if user is allowed to call an operation 281900f9497SJoseph Reynolds * 282900f9497SJoseph Reynolds * @param[in] operationPrivilegesRequired Privileges required 283900f9497SJoseph Reynolds * @param[in] userPrivileges Privileges the user has 284900f9497SJoseph Reynolds * 285900f9497SJoseph Reynolds * @return True if operation is allowed, false otherwise 286900f9497SJoseph Reynolds */ 287900f9497SJoseph Reynolds inline bool isOperationAllowedWithPrivileges( 288900f9497SJoseph Reynolds const std::vector<Privileges>& operationPrivilegesRequired, 289900f9497SJoseph Reynolds const Privileges& userPrivileges) 290900f9497SJoseph Reynolds { 291900f9497SJoseph Reynolds // If there are no privileges assigned, there are no privileges required 292900f9497SJoseph Reynolds if (operationPrivilegesRequired.empty()) 293900f9497SJoseph Reynolds { 294900f9497SJoseph Reynolds return true; 295900f9497SJoseph Reynolds } 2969eb808c1SEd Tanous for (const auto& requiredPrivileges : operationPrivilegesRequired) 297900f9497SJoseph Reynolds { 29854fbf177SAndrew Geissler BMCWEB_LOG_DEBUG << "Checking operation privileges..."; 299900f9497SJoseph Reynolds if (userPrivileges.isSupersetOf(requiredPrivileges)) 300900f9497SJoseph Reynolds { 30154fbf177SAndrew Geissler BMCWEB_LOG_DEBUG << "...success"; 302900f9497SJoseph Reynolds return true; 303900f9497SJoseph Reynolds } 304900f9497SJoseph Reynolds } 305900f9497SJoseph Reynolds return false; 306900f9497SJoseph Reynolds } 307900f9497SJoseph Reynolds 30843a095abSBorawski.Lukasz /** 309aecb47a4SBorawski.Lukasz * @brief Checks if given privileges allow to call an HTTP method 310aecb47a4SBorawski.Lukasz * 311aecb47a4SBorawski.Lukasz * @param[in] method HTTP method 312aecb47a4SBorawski.Lukasz * @param[in] user Privileges 313aecb47a4SBorawski.Lukasz * 314aecb47a4SBorawski.Lukasz * @return True if method allowed, false otherwise 31543a095abSBorawski.Lukasz * 316aecb47a4SBorawski.Lukasz */ 317e0d918bcSEd Tanous inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, 3183ebd75f7SEd Tanous const OperationMap& operationMap, 3191abe55efSEd Tanous const Privileges& userPrivileges) 3201abe55efSEd Tanous { 3213ebd75f7SEd Tanous const auto& it = operationMap.find(method); 3221abe55efSEd Tanous if (it == operationMap.end()) 3231abe55efSEd Tanous { 32443a095abSBorawski.Lukasz return false; 32543a095abSBorawski.Lukasz } 326aecb47a4SBorawski.Lukasz 327900f9497SJoseph Reynolds return isOperationAllowedWithPrivileges(it->second, userPrivileges); 32886e1b661SBorawski.Lukasz } 329aecb47a4SBorawski.Lukasz 33086e1b661SBorawski.Lukasz } // namespace redfish 331