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 18c94ad49bSEd Tanous #include <logging.h> 19f00032dbSTanous 20f00032dbSTanous #include <array> 21aecb47a4SBorawski.Lukasz #include <bitset> 22f00032dbSTanous #include <boost/beast/http/verb.hpp> 23aecb47a4SBorawski.Lukasz #include <boost/container/flat_map.hpp> 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 */ 47a692779fSEd Tanous static const std::vector<std::string> privilegeNames{basePrivileges.begin(), 48a692779fSEd Tanous basePrivileges.end()}; 4943a095abSBorawski.Lukasz 5086e1b661SBorawski.Lukasz /** 51aecb47a4SBorawski.Lukasz * @brief Redfish privileges 52aecb47a4SBorawski.Lukasz * 53900f9497SJoseph Reynolds * This implements a set of Redfish privileges. These directly represent 54900f9497SJoseph Reynolds * user privileges and help represent entity privileges. 55aecb47a4SBorawski.Lukasz * 5655c7b7a2SEd Tanous * Each incoming Connection requires a comparison between privileges held 57aecb47a4SBorawski.Lukasz * by the user issuing a request and the target entity's privileges. 58aecb47a4SBorawski.Lukasz * 59aecb47a4SBorawski.Lukasz * To ensure best runtime performance of this comparison, privileges 60aecb47a4SBorawski.Lukasz * are represented as bitsets. Each bit in the bitset corresponds to a 61aecb47a4SBorawski.Lukasz * unique privilege name. 62aecb47a4SBorawski.Lukasz * 63aecb47a4SBorawski.Lukasz * A bit is set if the privilege is required (entity domain) or granted 64aecb47a4SBorawski.Lukasz * (user domain) and false otherwise. 65aecb47a4SBorawski.Lukasz * 6686e1b661SBorawski.Lukasz */ 671abe55efSEd Tanous class Privileges 681abe55efSEd Tanous { 69aecb47a4SBorawski.Lukasz public: 70aecb47a4SBorawski.Lukasz /** 7143a095abSBorawski.Lukasz * @brief Constructs object without any privileges active 7243a095abSBorawski.Lukasz * 7343a095abSBorawski.Lukasz */ 7443a095abSBorawski.Lukasz Privileges() = default; 7543a095abSBorawski.Lukasz 7643a095abSBorawski.Lukasz /** 7743a095abSBorawski.Lukasz * @brief Constructs object with given privileges active 7843a095abSBorawski.Lukasz * 7943a095abSBorawski.Lukasz * @param[in] privilegeList List of privileges to be activated 8043a095abSBorawski.Lukasz * 8143a095abSBorawski.Lukasz */ 821abe55efSEd Tanous Privileges(std::initializer_list<const char*> privilegeList) 831abe55efSEd Tanous { 841abe55efSEd Tanous for (const char* privilege : privilegeList) 851abe55efSEd Tanous { 861abe55efSEd Tanous if (!setSinglePrivilege(privilege)) 871abe55efSEd Tanous { 8855c7b7a2SEd Tanous BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege 893ebd75f7SEd Tanous << "in constructor"; 9043a095abSBorawski.Lukasz } 9143a095abSBorawski.Lukasz } 923ebd75f7SEd Tanous } 93aecb47a4SBorawski.Lukasz 94aecb47a4SBorawski.Lukasz /** 95aecb47a4SBorawski.Lukasz * @brief Sets given privilege in the bitset 96aecb47a4SBorawski.Lukasz * 97aecb47a4SBorawski.Lukasz * @param[in] privilege Privilege to be set 98aecb47a4SBorawski.Lukasz * 99aecb47a4SBorawski.Lukasz * @return None 10043a095abSBorawski.Lukasz * 101aecb47a4SBorawski.Lukasz */ 1021abe55efSEd Tanous bool setSinglePrivilege(const char* privilege) 1031abe55efSEd Tanous { 104271584abSEd Tanous for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 1051abe55efSEd Tanous searchIndex++) 1061abe55efSEd Tanous { 1071abe55efSEd Tanous if (privilege == privilegeNames[searchIndex]) 1081abe55efSEd Tanous { 10955c7b7a2SEd Tanous privilegeBitset.set(searchIndex); 1103ebd75f7SEd Tanous return true; 111aecb47a4SBorawski.Lukasz } 112a692779fSEd Tanous } 113aecb47a4SBorawski.Lukasz 1143ebd75f7SEd Tanous return false; 1153ebd75f7SEd Tanous } 1163ebd75f7SEd Tanous 1173ebd75f7SEd Tanous /** 1183ebd75f7SEd Tanous * @brief Sets given privilege in the bitset 1193ebd75f7SEd Tanous * 1203ebd75f7SEd Tanous * @param[in] privilege Privilege to be set 1213ebd75f7SEd Tanous * 1223ebd75f7SEd Tanous * @return None 1233ebd75f7SEd Tanous * 1243ebd75f7SEd Tanous */ 1251abe55efSEd Tanous bool setSinglePrivilege(const std::string& privilege) 1261abe55efSEd Tanous { 1273ebd75f7SEd Tanous return setSinglePrivilege(privilege.c_str()); 128aecb47a4SBorawski.Lukasz } 129aecb47a4SBorawski.Lukasz 130aecb47a4SBorawski.Lukasz /** 131900f9497SJoseph Reynolds * @brief Resets the given privilege in the bitset 132900f9497SJoseph Reynolds * 133900f9497SJoseph Reynolds * @param[in] privilege Privilege to be reset 134900f9497SJoseph Reynolds * 135900f9497SJoseph Reynolds * @return None 136900f9497SJoseph Reynolds * 137900f9497SJoseph Reynolds */ 138900f9497SJoseph Reynolds bool resetSinglePrivilege(const char* privilege) 139900f9497SJoseph Reynolds { 140900f9497SJoseph Reynolds for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); 141900f9497SJoseph Reynolds searchIndex++) 142900f9497SJoseph Reynolds { 143900f9497SJoseph Reynolds if (privilege == privilegeNames[searchIndex]) 144900f9497SJoseph Reynolds { 145900f9497SJoseph Reynolds privilegeBitset.reset(searchIndex); 146900f9497SJoseph Reynolds return true; 147900f9497SJoseph Reynolds } 148900f9497SJoseph Reynolds } 149900f9497SJoseph Reynolds return false; 150900f9497SJoseph Reynolds } 151900f9497SJoseph Reynolds 152900f9497SJoseph Reynolds /** 153aecb47a4SBorawski.Lukasz * @brief Retrieves names of all active privileges for a given type 154aecb47a4SBorawski.Lukasz * 155aecb47a4SBorawski.Lukasz * @param[in] type Base or OEM 156aecb47a4SBorawski.Lukasz * 1573ebd75f7SEd Tanous * @return Vector of active privileges. Pointers are valid until 158a692779fSEd Tanous * the setSinglePrivilege is called, or the Privilege structure is destroyed 15943a095abSBorawski.Lukasz * 160aecb47a4SBorawski.Lukasz */ 1611abe55efSEd Tanous std::vector<const std::string*> 1621abe55efSEd Tanous getActivePrivilegeNames(const PrivilegeType type) const 1631abe55efSEd Tanous { 1643ebd75f7SEd Tanous std::vector<const std::string*> activePrivileges; 165aecb47a4SBorawski.Lukasz 166271584abSEd Tanous size_t searchIndex = 0; 167271584abSEd Tanous size_t endIndex = basePrivilegeCount; 1681abe55efSEd Tanous if (type == PrivilegeType::OEM) 1691abe55efSEd Tanous { 17055c7b7a2SEd Tanous searchIndex = basePrivilegeCount - 1; 17155c7b7a2SEd Tanous endIndex = privilegeNames.size(); 172a692779fSEd Tanous } 173a692779fSEd Tanous 1741abe55efSEd Tanous for (; searchIndex < endIndex; searchIndex++) 1751abe55efSEd Tanous { 1761abe55efSEd Tanous if (privilegeBitset.test(searchIndex)) 1771abe55efSEd Tanous { 17855c7b7a2SEd Tanous activePrivileges.emplace_back(&privilegeNames[searchIndex]); 179aecb47a4SBorawski.Lukasz } 180aecb47a4SBorawski.Lukasz } 181a692779fSEd Tanous 182aecb47a4SBorawski.Lukasz return activePrivileges; 183aecb47a4SBorawski.Lukasz } 184aecb47a4SBorawski.Lukasz 1853ebd75f7SEd Tanous /** 1863ebd75f7SEd Tanous * @brief Determines if this Privilege set is a superset of the given 1873ebd75f7SEd Tanous * privilege set 1883ebd75f7SEd Tanous * 1893ebd75f7SEd Tanous * @param[in] privilege Privilege to be checked 1903ebd75f7SEd Tanous * 1913ebd75f7SEd Tanous * @return None 1923ebd75f7SEd Tanous * 1933ebd75f7SEd Tanous */ 1941abe55efSEd Tanous bool isSupersetOf(const Privileges& p) const 1951abe55efSEd Tanous { 196a692779fSEd Tanous return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset; 1973ebd75f7SEd Tanous } 1983ebd75f7SEd Tanous 199*3bf4e632SJoseph Reynolds /** 200*3bf4e632SJoseph Reynolds * @brief Returns the intersection of two Privilege sets. 201*3bf4e632SJoseph Reynolds * 202*3bf4e632SJoseph Reynolds * @param[in] privilege Privilege set to intersect with. 203*3bf4e632SJoseph Reynolds * 204*3bf4e632SJoseph Reynolds * @return The new Privilege set. 205*3bf4e632SJoseph Reynolds * 206*3bf4e632SJoseph Reynolds */ 207*3bf4e632SJoseph Reynolds Privileges intersection(const Privileges& p) const 208*3bf4e632SJoseph Reynolds { 209*3bf4e632SJoseph Reynolds return Privileges{privilegeBitset & p.privilegeBitset}; 210*3bf4e632SJoseph Reynolds } 211*3bf4e632SJoseph Reynolds 21286e1b661SBorawski.Lukasz private: 213*3bf4e632SJoseph Reynolds Privileges(const std::bitset<maxPrivilegeCount>& p) : privilegeBitset{p} 214*3bf4e632SJoseph Reynolds { 215*3bf4e632SJoseph Reynolds } 21655c7b7a2SEd Tanous std::bitset<maxPrivilegeCount> privilegeBitset = 0; 21786e1b661SBorawski.Lukasz }; 21886e1b661SBorawski.Lukasz 2196f359568SRatan Gupta inline const Privileges& getUserPrivileges(const std::string& userRole) 2206f359568SRatan Gupta { 2216f359568SRatan Gupta // Redfish privilege : Administrator 2226f359568SRatan Gupta if (userRole == "priv-admin") 2236f359568SRatan Gupta { 2246f359568SRatan Gupta static Privileges admin{"Login", "ConfigureManager", "ConfigureSelf", 2256f359568SRatan Gupta "ConfigureUsers", "ConfigureComponents"}; 2266f359568SRatan Gupta return admin; 2276f359568SRatan Gupta } 2286f359568SRatan Gupta else if (userRole == "priv-operator") 2296f359568SRatan Gupta { 2306f359568SRatan Gupta // Redfish privilege : Operator 2316f359568SRatan Gupta static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"}; 2326f359568SRatan Gupta return op; 2336f359568SRatan Gupta } 234d7e08029Sjayaprakash Mutyala else if (userRole == "priv-user") 2356f359568SRatan Gupta { 2366f359568SRatan Gupta // Redfish privilege : Readonly 2376f359568SRatan Gupta static Privileges readOnly{"Login", "ConfigureSelf"}; 2386f359568SRatan Gupta return readOnly; 2396f359568SRatan Gupta } 240d7e08029Sjayaprakash Mutyala else 241d7e08029Sjayaprakash Mutyala { 242d7e08029Sjayaprakash Mutyala // Redfish privilege : NoAccess 243d7e08029Sjayaprakash Mutyala static Privileges noaccess; 244d7e08029Sjayaprakash Mutyala return noaccess; 245d7e08029Sjayaprakash Mutyala } 2466f359568SRatan Gupta } 2476f359568SRatan Gupta 248900f9497SJoseph Reynolds /** 249900f9497SJoseph Reynolds * @brief The OperationMap represents the privileges required for a 250900f9497SJoseph Reynolds * single entity (URI). It maps from the allowable verbs to the 251900f9497SJoseph Reynolds * privileges required to use that operation. 252900f9497SJoseph Reynolds * 253900f9497SJoseph Reynolds * This represents the Redfish "Privilege AND and OR syntax" as given 254900f9497SJoseph Reynolds * in the spec and shown in the Privilege Registry. This does not 255900f9497SJoseph Reynolds * implement any Redfish property overrides, subordinate overrides, or 256900f9497SJoseph Reynolds * resource URI overrides. This does not implement the limitation of 257900f9497SJoseph Reynolds * the ConfigureSelf privilege to operate only on your own account or 258900f9497SJoseph Reynolds * session. 259900f9497SJoseph Reynolds **/ 260e0d918bcSEd Tanous using OperationMap = boost::container::flat_map<boost::beast::http::verb, 261e0d918bcSEd Tanous std::vector<Privileges>>; 26243a095abSBorawski.Lukasz 263900f9497SJoseph Reynolds /* @brief Checks if user is allowed to call an operation 264900f9497SJoseph Reynolds * 265900f9497SJoseph Reynolds * @param[in] operationPrivilegesRequired Privileges required 266900f9497SJoseph Reynolds * @param[in] userPrivileges Privileges the user has 267900f9497SJoseph Reynolds * 268900f9497SJoseph Reynolds * @return True if operation is allowed, false otherwise 269900f9497SJoseph Reynolds */ 270900f9497SJoseph Reynolds inline bool isOperationAllowedWithPrivileges( 271900f9497SJoseph Reynolds const std::vector<Privileges>& operationPrivilegesRequired, 272900f9497SJoseph Reynolds const Privileges& userPrivileges) 273900f9497SJoseph Reynolds { 274900f9497SJoseph Reynolds // If there are no privileges assigned, there are no privileges required 275900f9497SJoseph Reynolds if (operationPrivilegesRequired.empty()) 276900f9497SJoseph Reynolds { 277900f9497SJoseph Reynolds return true; 278900f9497SJoseph Reynolds } 279900f9497SJoseph Reynolds for (auto& requiredPrivileges : operationPrivilegesRequired) 280900f9497SJoseph Reynolds { 281900f9497SJoseph Reynolds BMCWEB_LOG_ERROR << "Checking operation privileges..."; 282900f9497SJoseph Reynolds if (userPrivileges.isSupersetOf(requiredPrivileges)) 283900f9497SJoseph Reynolds { 284900f9497SJoseph Reynolds BMCWEB_LOG_ERROR << "...success"; 285900f9497SJoseph Reynolds return true; 286900f9497SJoseph Reynolds } 287900f9497SJoseph Reynolds } 288900f9497SJoseph Reynolds return false; 289900f9497SJoseph Reynolds } 290900f9497SJoseph Reynolds 29143a095abSBorawski.Lukasz /** 292aecb47a4SBorawski.Lukasz * @brief Checks if given privileges allow to call an HTTP method 293aecb47a4SBorawski.Lukasz * 294aecb47a4SBorawski.Lukasz * @param[in] method HTTP method 295aecb47a4SBorawski.Lukasz * @param[in] user Privileges 296aecb47a4SBorawski.Lukasz * 297aecb47a4SBorawski.Lukasz * @return True if method allowed, false otherwise 29843a095abSBorawski.Lukasz * 299aecb47a4SBorawski.Lukasz */ 300e0d918bcSEd Tanous inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, 3013ebd75f7SEd Tanous const OperationMap& operationMap, 3021abe55efSEd Tanous const Privileges& userPrivileges) 3031abe55efSEd Tanous { 3043ebd75f7SEd Tanous const auto& it = operationMap.find(method); 3051abe55efSEd Tanous if (it == operationMap.end()) 3061abe55efSEd Tanous { 30743a095abSBorawski.Lukasz return false; 30843a095abSBorawski.Lukasz } 309aecb47a4SBorawski.Lukasz 310900f9497SJoseph Reynolds return isOperationAllowedWithPrivileges(it->second, userPrivileges); 31186e1b661SBorawski.Lukasz } 312aecb47a4SBorawski.Lukasz 3133ebd75f7SEd Tanous /** 3143ebd75f7SEd Tanous * @brief Checks if a user is allowed to call an HTTP method 3153ebd75f7SEd Tanous * 3163ebd75f7SEd Tanous * @param[in] method HTTP method 3173ebd75f7SEd Tanous * @param[in] user Username 3183ebd75f7SEd Tanous * 3193ebd75f7SEd Tanous * @return True if method allowed, false otherwise 3203ebd75f7SEd Tanous * 3213ebd75f7SEd Tanous */ 322e0d918bcSEd Tanous inline bool isMethodAllowedForUser(const boost::beast::http::verb method, 3233ebd75f7SEd Tanous const OperationMap& operationMap, 3241abe55efSEd Tanous const std::string& user) 3251abe55efSEd Tanous { 3263ebd75f7SEd Tanous // TODO: load user privileges from configuration as soon as its available 3273ebd75f7SEd Tanous // now we are granting all privileges to everyone. 3283ebd75f7SEd Tanous Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf", 3293ebd75f7SEd Tanous "ConfigureUsers", "ConfigureComponents"}; 3303ebd75f7SEd Tanous 3313ebd75f7SEd Tanous return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges); 33286e1b661SBorawski.Lukasz } 33386e1b661SBorawski.Lukasz 33486e1b661SBorawski.Lukasz } // namespace redfish 335