xref: /openbmc/bmcweb/features/redfish/include/privileges.hpp (revision ac25adb8d491342fc5fd4e189c58b79be6f5835a)
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"
193e72c202SNinad 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  */
48*ac25adb8SEd Tanous constexpr std::array<std::string_view, 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 
573e72c202SNinad Palsule /**
583e72c202SNinad Palsule  * @brief A vector of all privilege names and their indexes
593e72c202SNinad Palsule  * The privilege "OpenBMCHostConsole" is added to users who are members of the
603e72c202SNinad Palsule  * "hostconsole" user group. This privilege is required to access the host
613e72c202SNinad Palsule  * console.
623e72c202SNinad Palsule  */
63*ac25adb8SEd Tanous constexpr std::array<std::string_view, maxPrivilegeCount> privilegeNames{
643e72c202SNinad Palsule     "Login",         "ConfigureManager", "ConfigureComponents",
653e72c202SNinad 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             {
10562598e31SEd Tanous                 BMCWEB_LOG_CRITICAL("Unable to set privilege {} in constructor",
10662598e31SEd Tanous                                     privilege);
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 
2233e72c202SNinad Palsule inline Privileges getUserPrivileges(const persistent_data::UserSession& session)
2243e72c202SNinad Palsule {
2253e72c202SNinad Palsule     // default to no access
2263e72c202SNinad Palsule     Privileges privs;
2273e72c202SNinad Palsule 
2283e72c202SNinad Palsule     // Check if user is member of hostconsole group
2293e72c202SNinad Palsule     for (const auto& userGroup : session.userGroups)
2303e72c202SNinad Palsule     {
2313e72c202SNinad Palsule         if (userGroup == "hostconsole")
2323e72c202SNinad Palsule         {
2333e72c202SNinad Palsule             // Redfish privilege : host console access
2343e72c202SNinad Palsule             privs.setSinglePrivilege("OpenBMCHostConsole");
2353e72c202SNinad Palsule             break;
2363e72c202SNinad Palsule         }
2373e72c202SNinad Palsule     }
2383e72c202SNinad Palsule 
2393e72c202SNinad Palsule     if (session.userRole == "priv-admin")
2406f359568SRatan Gupta     {
2416f359568SRatan Gupta         // Redfish privilege : Administrator
2423e72c202SNinad Palsule         privs.setSinglePrivilege("Login");
2433e72c202SNinad Palsule         privs.setSinglePrivilege("ConfigureManager");
2443e72c202SNinad Palsule         privs.setSinglePrivilege("ConfigureSelf");
2453e72c202SNinad Palsule         privs.setSinglePrivilege("ConfigureUsers");
2463e72c202SNinad Palsule         privs.setSinglePrivilege("ConfigureComponents");
2476f359568SRatan Gupta     }
2483e72c202SNinad Palsule     else if (session.userRole == "priv-operator")
2496f359568SRatan Gupta     {
2506f359568SRatan Gupta         // Redfish privilege : Operator
2513e72c202SNinad Palsule         privs.setSinglePrivilege("Login");
2523e72c202SNinad Palsule         privs.setSinglePrivilege("ConfigureSelf");
2533e72c202SNinad Palsule         privs.setSinglePrivilege("ConfigureComponents");
2546f359568SRatan Gupta     }
2553e72c202SNinad Palsule     else if (session.userRole == "priv-user")
2566f359568SRatan Gupta     {
2576f359568SRatan Gupta         // Redfish privilege : Readonly
2583e72c202SNinad Palsule         privs.setSinglePrivilege("Login");
2593e72c202SNinad Palsule         privs.setSinglePrivilege("ConfigureSelf");
2606f359568SRatan Gupta     }
2613e72c202SNinad Palsule 
2623e72c202SNinad 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     {
29862598e31SEd Tanous         BMCWEB_LOG_DEBUG("Checking operation privileges...");
299900f9497SJoseph Reynolds         if (userPrivileges.isSupersetOf(requiredPrivileges))
300900f9497SJoseph Reynolds         {
30162598e31SEd Tanous             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