xref: /openbmc/bmcweb/features/redfish/include/privileges.hpp (revision 6f3595683739a6528e98b38cc82afbc60f9fda34)
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 
18f00032dbSTanous #include <crow/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 
41b01bf299SEd Tanous constexpr const int basePrivilegeCount = basePrivileges.size();
42a692779fSEd Tanous 
43a692779fSEd Tanous /** @brief Max number of privileges per type  */
44b01bf299SEd Tanous constexpr const int 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  *
53aecb47a4SBorawski.Lukasz  *        Entity privileges and user privileges are represented by this class.
54aecb47a4SBorawski.Lukasz  *
5555c7b7a2SEd Tanous  *        Each incoming Connection requires a comparison between privileges held
56aecb47a4SBorawski.Lukasz  *        by the user issuing a request and the target entity's privileges.
57aecb47a4SBorawski.Lukasz  *
58aecb47a4SBorawski.Lukasz  *        To ensure best runtime performance of this comparison, privileges
59aecb47a4SBorawski.Lukasz  *        are represented as bitsets. Each bit in the bitset corresponds to a
60aecb47a4SBorawski.Lukasz  *        unique privilege name.
61aecb47a4SBorawski.Lukasz  *
62aecb47a4SBorawski.Lukasz  *        A bit is set if the privilege is required (entity domain) or granted
63aecb47a4SBorawski.Lukasz  *        (user domain) and false otherwise.
64aecb47a4SBorawski.Lukasz  *
6586e1b661SBorawski.Lukasz  */
661abe55efSEd Tanous class Privileges
671abe55efSEd Tanous {
68aecb47a4SBorawski.Lukasz   public:
69aecb47a4SBorawski.Lukasz     /**
7043a095abSBorawski.Lukasz      * @brief Constructs object without any privileges active
7143a095abSBorawski.Lukasz      *
7243a095abSBorawski.Lukasz      */
7343a095abSBorawski.Lukasz     Privileges() = default;
7443a095abSBorawski.Lukasz 
7543a095abSBorawski.Lukasz     /**
7643a095abSBorawski.Lukasz      * @brief Constructs object with given privileges active
7743a095abSBorawski.Lukasz      *
7843a095abSBorawski.Lukasz      * @param[in] privilegeList  List of privileges to be activated
7943a095abSBorawski.Lukasz      *
8043a095abSBorawski.Lukasz      */
811abe55efSEd Tanous     Privileges(std::initializer_list<const char*> privilegeList)
821abe55efSEd Tanous     {
831abe55efSEd Tanous         for (const char* privilege : privilegeList)
841abe55efSEd Tanous         {
851abe55efSEd Tanous             if (!setSinglePrivilege(privilege))
861abe55efSEd Tanous             {
8755c7b7a2SEd Tanous                 BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege
883ebd75f7SEd Tanous                                     << "in constructor";
8943a095abSBorawski.Lukasz             }
9043a095abSBorawski.Lukasz         }
913ebd75f7SEd Tanous     }
92aecb47a4SBorawski.Lukasz 
93aecb47a4SBorawski.Lukasz     /**
94aecb47a4SBorawski.Lukasz      * @brief Sets given privilege in the bitset
95aecb47a4SBorawski.Lukasz      *
96aecb47a4SBorawski.Lukasz      * @param[in] privilege  Privilege to be set
97aecb47a4SBorawski.Lukasz      *
98aecb47a4SBorawski.Lukasz      * @return               None
9943a095abSBorawski.Lukasz      *
100aecb47a4SBorawski.Lukasz      */
1011abe55efSEd Tanous     bool setSinglePrivilege(const char* privilege)
1021abe55efSEd Tanous     {
103b01bf299SEd Tanous         for (int searchIndex = 0; searchIndex < privilegeNames.size();
1041abe55efSEd Tanous              searchIndex++)
1051abe55efSEd Tanous         {
1061abe55efSEd Tanous             if (privilege == privilegeNames[searchIndex])
1071abe55efSEd Tanous             {
10855c7b7a2SEd Tanous                 privilegeBitset.set(searchIndex);
1093ebd75f7SEd Tanous                 return true;
110aecb47a4SBorawski.Lukasz             }
111a692779fSEd Tanous         }
112aecb47a4SBorawski.Lukasz 
1133ebd75f7SEd Tanous         return false;
1143ebd75f7SEd Tanous     }
1153ebd75f7SEd Tanous 
1163ebd75f7SEd Tanous     /**
1173ebd75f7SEd Tanous      * @brief Sets given privilege in the bitset
1183ebd75f7SEd Tanous      *
1193ebd75f7SEd Tanous      * @param[in] privilege  Privilege to be set
1203ebd75f7SEd Tanous      *
1213ebd75f7SEd Tanous      * @return               None
1223ebd75f7SEd Tanous      *
1233ebd75f7SEd Tanous      */
1241abe55efSEd Tanous     bool setSinglePrivilege(const std::string& privilege)
1251abe55efSEd Tanous     {
1263ebd75f7SEd Tanous         return setSinglePrivilege(privilege.c_str());
127aecb47a4SBorawski.Lukasz     }
128aecb47a4SBorawski.Lukasz 
129aecb47a4SBorawski.Lukasz     /**
130aecb47a4SBorawski.Lukasz      * @brief Retrieves names of all active privileges for a given type
131aecb47a4SBorawski.Lukasz      *
132aecb47a4SBorawski.Lukasz      * @param[in] type    Base or OEM
133aecb47a4SBorawski.Lukasz      *
1343ebd75f7SEd Tanous      * @return            Vector of active privileges.  Pointers are valid until
135a692779fSEd Tanous      * the setSinglePrivilege is called, or the Privilege structure is destroyed
13643a095abSBorawski.Lukasz      *
137aecb47a4SBorawski.Lukasz      */
1381abe55efSEd Tanous     std::vector<const std::string*>
1391abe55efSEd Tanous         getActivePrivilegeNames(const PrivilegeType type) const
1401abe55efSEd Tanous     {
1413ebd75f7SEd Tanous         std::vector<const std::string*> activePrivileges;
142aecb47a4SBorawski.Lukasz 
143b01bf299SEd Tanous         int searchIndex = 0;
144b01bf299SEd Tanous         int endIndex = basePrivilegeCount;
1451abe55efSEd Tanous         if (type == PrivilegeType::OEM)
1461abe55efSEd Tanous         {
14755c7b7a2SEd Tanous             searchIndex = basePrivilegeCount - 1;
14855c7b7a2SEd Tanous             endIndex = privilegeNames.size();
149a692779fSEd Tanous         }
150a692779fSEd Tanous 
1511abe55efSEd Tanous         for (; searchIndex < endIndex; searchIndex++)
1521abe55efSEd Tanous         {
1531abe55efSEd Tanous             if (privilegeBitset.test(searchIndex))
1541abe55efSEd Tanous             {
15555c7b7a2SEd Tanous                 activePrivileges.emplace_back(&privilegeNames[searchIndex]);
156aecb47a4SBorawski.Lukasz             }
157aecb47a4SBorawski.Lukasz         }
158a692779fSEd Tanous 
159aecb47a4SBorawski.Lukasz         return activePrivileges;
160aecb47a4SBorawski.Lukasz     }
161aecb47a4SBorawski.Lukasz 
1623ebd75f7SEd Tanous     /**
1633ebd75f7SEd Tanous      * @brief Determines if this Privilege set is a superset of the given
1643ebd75f7SEd Tanous      * privilege set
1653ebd75f7SEd Tanous      *
1663ebd75f7SEd Tanous      * @param[in] privilege  Privilege to be checked
1673ebd75f7SEd Tanous      *
1683ebd75f7SEd Tanous      * @return               None
1693ebd75f7SEd Tanous      *
1703ebd75f7SEd Tanous      */
1711abe55efSEd Tanous     bool isSupersetOf(const Privileges& p) const
1721abe55efSEd Tanous     {
173a692779fSEd Tanous         return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset;
1743ebd75f7SEd Tanous     }
1753ebd75f7SEd Tanous 
17686e1b661SBorawski.Lukasz   private:
17755c7b7a2SEd Tanous     std::bitset<maxPrivilegeCount> privilegeBitset = 0;
17886e1b661SBorawski.Lukasz };
17986e1b661SBorawski.Lukasz 
180*6f359568SRatan Gupta inline const Privileges& getUserPrivileges(const std::string& userRole)
181*6f359568SRatan Gupta {
182*6f359568SRatan Gupta     // Redfish privilege : Administrator
183*6f359568SRatan Gupta     if (userRole == "priv-admin")
184*6f359568SRatan Gupta     {
185*6f359568SRatan Gupta         static Privileges admin{"Login", "ConfigureManager", "ConfigureSelf",
186*6f359568SRatan Gupta                                 "ConfigureUsers", "ConfigureComponents"};
187*6f359568SRatan Gupta         return admin;
188*6f359568SRatan Gupta     }
189*6f359568SRatan Gupta     else if (userRole == "priv-operator")
190*6f359568SRatan Gupta     {
191*6f359568SRatan Gupta         // Redfish privilege : Operator
192*6f359568SRatan Gupta         static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"};
193*6f359568SRatan Gupta         return op;
194*6f359568SRatan Gupta     }
195*6f359568SRatan Gupta     else
196*6f359568SRatan Gupta     {
197*6f359568SRatan Gupta         // Redfish privilege : Readonly
198*6f359568SRatan Gupta         static Privileges readOnly{"Login", "ConfigureSelf"};
199*6f359568SRatan Gupta         return readOnly;
200*6f359568SRatan Gupta     }
201*6f359568SRatan Gupta }
202*6f359568SRatan Gupta 
203e0d918bcSEd Tanous using OperationMap = boost::container::flat_map<boost::beast::http::verb,
204e0d918bcSEd Tanous                                                 std::vector<Privileges>>;
20543a095abSBorawski.Lukasz 
20643a095abSBorawski.Lukasz /**
207aecb47a4SBorawski.Lukasz  * @brief Checks if given privileges allow to call an HTTP method
208aecb47a4SBorawski.Lukasz  *
209aecb47a4SBorawski.Lukasz  * @param[in] method       HTTP method
210aecb47a4SBorawski.Lukasz  * @param[in] user         Privileges
211aecb47a4SBorawski.Lukasz  *
212aecb47a4SBorawski.Lukasz  * @return                 True if method allowed, false otherwise
21343a095abSBorawski.Lukasz  *
214aecb47a4SBorawski.Lukasz  */
215e0d918bcSEd Tanous inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method,
2163ebd75f7SEd Tanous                                           const OperationMap& operationMap,
2171abe55efSEd Tanous                                           const Privileges& userPrivileges)
2181abe55efSEd Tanous {
2193ebd75f7SEd Tanous     const auto& it = operationMap.find(method);
2201abe55efSEd Tanous     if (it == operationMap.end())
2211abe55efSEd Tanous     {
22243a095abSBorawski.Lukasz         return false;
22343a095abSBorawski.Lukasz     }
224aecb47a4SBorawski.Lukasz 
2253ebd75f7SEd Tanous     // If there are no privileges assigned, assume no privileges required
2261abe55efSEd Tanous     if (it->second.empty())
2271abe55efSEd Tanous     {
22843a095abSBorawski.Lukasz         return true;
22943a095abSBorawski.Lukasz     }
2303ebd75f7SEd Tanous 
2311abe55efSEd Tanous     for (auto& requiredPrivileges : it->second)
2321abe55efSEd Tanous     {
2331abe55efSEd Tanous         if (userPrivileges.isSupersetOf(requiredPrivileges))
2341abe55efSEd Tanous         {
2353ebd75f7SEd Tanous             return true;
2363ebd75f7SEd Tanous         }
2373ebd75f7SEd Tanous     }
23843a095abSBorawski.Lukasz     return false;
23986e1b661SBorawski.Lukasz }
240aecb47a4SBorawski.Lukasz 
2413ebd75f7SEd Tanous /**
2423ebd75f7SEd Tanous  * @brief Checks if a user is allowed to call an HTTP method
2433ebd75f7SEd Tanous  *
2443ebd75f7SEd Tanous  * @param[in] method       HTTP method
2453ebd75f7SEd Tanous  * @param[in] user         Username
2463ebd75f7SEd Tanous  *
2473ebd75f7SEd Tanous  * @return                 True if method allowed, false otherwise
2483ebd75f7SEd Tanous  *
2493ebd75f7SEd Tanous  */
250e0d918bcSEd Tanous inline bool isMethodAllowedForUser(const boost::beast::http::verb method,
2513ebd75f7SEd Tanous                                    const OperationMap& operationMap,
2521abe55efSEd Tanous                                    const std::string& user)
2531abe55efSEd Tanous {
2543ebd75f7SEd Tanous     // TODO: load user privileges from configuration as soon as its available
2553ebd75f7SEd Tanous     // now we are granting all privileges to everyone.
2563ebd75f7SEd Tanous     Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf",
2573ebd75f7SEd Tanous                               "ConfigureUsers", "ConfigureComponents"};
2583ebd75f7SEd Tanous 
2593ebd75f7SEd Tanous     return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges);
26086e1b661SBorawski.Lukasz }
26186e1b661SBorawski.Lukasz 
26286e1b661SBorawski.Lukasz } // namespace redfish
263