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