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