xref: /openbmc/bmcweb/features/redfish/include/privileges.hpp (revision 40e9b92ec19acffb46f83a6e55b18974da5d708e)
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