xref: /openbmc/bmcweb/features/redfish/include/privileges.hpp (revision 43a095ab36b35bcb9192218fd11e0ff86ec63bf9)
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 
18aecb47a4SBorawski.Lukasz #include <bitset>
19aecb47a4SBorawski.Lukasz #include <cstdint>
20aecb47a4SBorawski.Lukasz #include "crow.h"
21aecb47a4SBorawski.Lukasz #include <boost/container/flat_map.hpp>
22*43a095abSBorawski.Lukasz #include <boost/container/flat_set.hpp>
23aecb47a4SBorawski.Lukasz #include <boost/optional.hpp>
24aecb47a4SBorawski.Lukasz 
2586e1b661SBorawski.Lukasz namespace redfish {
2686e1b661SBorawski.Lukasz 
27aecb47a4SBorawski.Lukasz class PrivilegeProvider;
28aecb47a4SBorawski.Lukasz 
29aecb47a4SBorawski.Lukasz enum class PrivilegeType { BASE, OEM };
30aecb47a4SBorawski.Lukasz 
31aecb47a4SBorawski.Lukasz /** @brief Max number of privileges per type  */
32aecb47a4SBorawski.Lukasz constexpr const size_t MAX_PRIVILEGE_COUNT = 32;
33*43a095abSBorawski.Lukasz 
34aecb47a4SBorawski.Lukasz using privilegeBitset = std::bitset<MAX_PRIVILEGE_COUNT>;
35aecb47a4SBorawski.Lukasz 
36*43a095abSBorawski.Lukasz /** @brief Number of mappings must be <= MAX_PRIVILEGE_COUNT */
37*43a095abSBorawski.Lukasz static const boost::container::flat_map<std::string, size_t>
38*43a095abSBorawski.Lukasz     basePrivNameToIndexMap = {{"Login", 0},
39*43a095abSBorawski.Lukasz                               {"ConfigureManager", 1},
40*43a095abSBorawski.Lukasz                               {"ConfigureComponents", 2},
41*43a095abSBorawski.Lukasz                               {"ConfigureSelf", 3},
42*43a095abSBorawski.Lukasz                               {"ConfigureUsers", 4}};
43*43a095abSBorawski.Lukasz 
44*43a095abSBorawski.Lukasz /** @brief Number of mappings must be <= MAX_PRIVILEGE_COUNT */
45*43a095abSBorawski.Lukasz static const boost::container::flat_map<std::string, size_t>
46*43a095abSBorawski.Lukasz     oemPrivNameToIndexMap = {};
47*43a095abSBorawski.Lukasz 
4886e1b661SBorawski.Lukasz /**
49aecb47a4SBorawski.Lukasz  * @brief Redfish privileges
50aecb47a4SBorawski.Lukasz  *
51aecb47a4SBorawski.Lukasz  *        Entity privileges and user privileges are represented by this class.
52aecb47a4SBorawski.Lukasz  *
53aecb47a4SBorawski.Lukasz  *        Each incoming connection requires a comparison between privileges held
54aecb47a4SBorawski.Lukasz  *        by the user issuing a request and the target entity's privileges.
55aecb47a4SBorawski.Lukasz  *
56aecb47a4SBorawski.Lukasz  *        To ensure best runtime performance of this comparison, privileges
57aecb47a4SBorawski.Lukasz  *        are represented as bitsets. Each bit in the bitset corresponds to a
58aecb47a4SBorawski.Lukasz  *        unique privilege name.
59aecb47a4SBorawski.Lukasz  *
60aecb47a4SBorawski.Lukasz  *        A bit is set if the privilege is required (entity domain) or granted
61aecb47a4SBorawski.Lukasz  *        (user domain) and false otherwise.
62aecb47a4SBorawski.Lukasz  *
6386e1b661SBorawski.Lukasz  */
64aecb47a4SBorawski.Lukasz class Privileges {
65aecb47a4SBorawski.Lukasz  public:
66aecb47a4SBorawski.Lukasz   /**
67*43a095abSBorawski.Lukasz    * @brief Constructs object without any privileges active
68*43a095abSBorawski.Lukasz    *
69*43a095abSBorawski.Lukasz    */
70*43a095abSBorawski.Lukasz   Privileges() = default;
71*43a095abSBorawski.Lukasz 
72*43a095abSBorawski.Lukasz   /**
73*43a095abSBorawski.Lukasz    * @brief Constructs object with given privileges active
74*43a095abSBorawski.Lukasz    *
75*43a095abSBorawski.Lukasz    * @param[in] privilegeList  List of privileges to be activated
76*43a095abSBorawski.Lukasz    *
77*43a095abSBorawski.Lukasz    */
78*43a095abSBorawski.Lukasz   Privileges(std::initializer_list<std::string> privilegeList) {
79*43a095abSBorawski.Lukasz     for (const auto& privilege : privilegeList) {
80*43a095abSBorawski.Lukasz       setSinglePrivilege(privilege);
81*43a095abSBorawski.Lukasz     }
82*43a095abSBorawski.Lukasz   }
83*43a095abSBorawski.Lukasz 
84*43a095abSBorawski.Lukasz   /**
85aecb47a4SBorawski.Lukasz    * @brief Retrieves the base privileges bitset
86aecb47a4SBorawski.Lukasz    *
87aecb47a4SBorawski.Lukasz    * @return          Bitset representation of base Redfish privileges
88*43a095abSBorawski.Lukasz    *
89aecb47a4SBorawski.Lukasz    */
90aecb47a4SBorawski.Lukasz   privilegeBitset getBasePrivilegeBitset() const { return basePrivilegeBitset; }
91aecb47a4SBorawski.Lukasz 
92aecb47a4SBorawski.Lukasz   /**
93aecb47a4SBorawski.Lukasz    * @brief Retrieves the OEM privileges bitset
94aecb47a4SBorawski.Lukasz    *
95aecb47a4SBorawski.Lukasz    * @return          Bitset representation of OEM Redfish privileges
96*43a095abSBorawski.Lukasz    *
97aecb47a4SBorawski.Lukasz    */
98aecb47a4SBorawski.Lukasz   privilegeBitset getOEMPrivilegeBitset() const { return oemPrivilegeBitset; }
99aecb47a4SBorawski.Lukasz 
100aecb47a4SBorawski.Lukasz   /**
101aecb47a4SBorawski.Lukasz    * @brief Sets given privilege in the bitset
102aecb47a4SBorawski.Lukasz    *
103aecb47a4SBorawski.Lukasz    * @param[in] privilege  Privilege to be set
104aecb47a4SBorawski.Lukasz    *
105aecb47a4SBorawski.Lukasz    * @return               None
106*43a095abSBorawski.Lukasz    *
107aecb47a4SBorawski.Lukasz    */
108aecb47a4SBorawski.Lukasz   void setSinglePrivilege(const std::string& privilege) {
109aecb47a4SBorawski.Lukasz     auto index = getBitsetIndexForPrivilege(privilege, PrivilegeType::BASE);
110aecb47a4SBorawski.Lukasz     if (index) {
111aecb47a4SBorawski.Lukasz       basePrivilegeBitset.set(*index);
112aecb47a4SBorawski.Lukasz       return;
113aecb47a4SBorawski.Lukasz     }
114aecb47a4SBorawski.Lukasz 
115aecb47a4SBorawski.Lukasz     index = getBitsetIndexForPrivilege(privilege, PrivilegeType::OEM);
116aecb47a4SBorawski.Lukasz     if (index) {
117aecb47a4SBorawski.Lukasz       oemPrivilegeBitset.set(*index);
118aecb47a4SBorawski.Lukasz     }
119aecb47a4SBorawski.Lukasz   }
120aecb47a4SBorawski.Lukasz 
121aecb47a4SBorawski.Lukasz   /**
122aecb47a4SBorawski.Lukasz    * @brief Retrieves names of all active privileges for a given type
123aecb47a4SBorawski.Lukasz    *
124aecb47a4SBorawski.Lukasz    * @param[in] type    Base or OEM
125aecb47a4SBorawski.Lukasz    *
126aecb47a4SBorawski.Lukasz    * @return            Vector of active privileges
127*43a095abSBorawski.Lukasz    *
128aecb47a4SBorawski.Lukasz    */
129aecb47a4SBorawski.Lukasz   std::vector<std::string> getActivePrivilegeNames(
130aecb47a4SBorawski.Lukasz       const PrivilegeType type) const {
131aecb47a4SBorawski.Lukasz     std::vector<std::string> activePrivileges;
132aecb47a4SBorawski.Lukasz 
133aecb47a4SBorawski.Lukasz     if (type == PrivilegeType::BASE) {
134aecb47a4SBorawski.Lukasz       for (const auto& pair : basePrivNameToIndexMap) {
135aecb47a4SBorawski.Lukasz         if (basePrivilegeBitset.test(pair.second)) {
136aecb47a4SBorawski.Lukasz           activePrivileges.emplace_back(pair.first);
137aecb47a4SBorawski.Lukasz         }
138aecb47a4SBorawski.Lukasz       }
139aecb47a4SBorawski.Lukasz     } else {
140aecb47a4SBorawski.Lukasz       for (const auto& pair : oemPrivNameToIndexMap) {
141aecb47a4SBorawski.Lukasz         if (oemPrivilegeBitset.test(pair.second)) {
142aecb47a4SBorawski.Lukasz           activePrivileges.emplace_back(pair.first);
143aecb47a4SBorawski.Lukasz         }
144aecb47a4SBorawski.Lukasz       }
145aecb47a4SBorawski.Lukasz     }
146aecb47a4SBorawski.Lukasz 
147aecb47a4SBorawski.Lukasz     return activePrivileges;
148aecb47a4SBorawski.Lukasz   }
149aecb47a4SBorawski.Lukasz 
15086e1b661SBorawski.Lukasz  private:
151aecb47a4SBorawski.Lukasz   boost::optional<size_t> getBitsetIndexForPrivilege(
152aecb47a4SBorawski.Lukasz       const std::string& privilege, const PrivilegeType type) const {
153aecb47a4SBorawski.Lukasz     if (type == PrivilegeType::BASE) {
154aecb47a4SBorawski.Lukasz       const auto pair = basePrivNameToIndexMap.find(privilege);
155aecb47a4SBorawski.Lukasz       if (pair != basePrivNameToIndexMap.end()) {
156aecb47a4SBorawski.Lukasz         return pair->second;
157aecb47a4SBorawski.Lukasz       }
158aecb47a4SBorawski.Lukasz     } else {
159aecb47a4SBorawski.Lukasz       const auto pair = oemPrivNameToIndexMap.find(privilege);
160aecb47a4SBorawski.Lukasz       if (pair != oemPrivNameToIndexMap.end()) {
161aecb47a4SBorawski.Lukasz         return pair->second;
162aecb47a4SBorawski.Lukasz       }
163aecb47a4SBorawski.Lukasz     }
164aecb47a4SBorawski.Lukasz 
165aecb47a4SBorawski.Lukasz     return boost::none;
166aecb47a4SBorawski.Lukasz   }
167aecb47a4SBorawski.Lukasz 
168aecb47a4SBorawski.Lukasz   privilegeBitset basePrivilegeBitset;
169aecb47a4SBorawski.Lukasz   privilegeBitset oemPrivilegeBitset;
170aecb47a4SBorawski.Lukasz 
171aecb47a4SBorawski.Lukasz   friend class PrivilegeProvider;
17286e1b661SBorawski.Lukasz };
17386e1b661SBorawski.Lukasz 
174*43a095abSBorawski.Lukasz using OperationMap =
175*43a095abSBorawski.Lukasz     boost::container::flat_map<crow::HTTPMethod, std::vector<Privileges>>;
176*43a095abSBorawski.Lukasz 
177*43a095abSBorawski.Lukasz /**
178*43a095abSBorawski.Lukasz  * @brief  Class used to store overrides privileges for Redfish
179*43a095abSBorawski.Lukasz  *         entities
180*43a095abSBorawski.Lukasz  *
181*43a095abSBorawski.Lukasz  */
182*43a095abSBorawski.Lukasz class EntityPrivilegesOverride {
183*43a095abSBorawski.Lukasz  protected:
184*43a095abSBorawski.Lukasz   /**
185*43a095abSBorawski.Lukasz    * @brief Constructs overrides object for given targets
186*43a095abSBorawski.Lukasz    *
187*43a095abSBorawski.Lukasz    * @param[in] operationMap Operation map to be applied for targets
188*43a095abSBorawski.Lukasz    * @param[in] targets      List of targets whOperation map to be applied for
189*43a095abSBorawski.Lukasz    * targets
190*43a095abSBorawski.Lukasz    *
191*43a095abSBorawski.Lukasz    */
192*43a095abSBorawski.Lukasz   EntityPrivilegesOverride(OperationMap&& operationMap,
193*43a095abSBorawski.Lukasz                            std::initializer_list<std::string>&& targets)
194*43a095abSBorawski.Lukasz       : operationMap(std::move(operationMap)), targets(std::move(targets)) {}
195*43a095abSBorawski.Lukasz 
196*43a095abSBorawski.Lukasz   const OperationMap operationMap;
197*43a095abSBorawski.Lukasz   const boost::container::flat_set<std::string> targets;
198*43a095abSBorawski.Lukasz };
199*43a095abSBorawski.Lukasz 
200*43a095abSBorawski.Lukasz class PropertyOverride : public EntityPrivilegesOverride {
201*43a095abSBorawski.Lukasz  public:
202*43a095abSBorawski.Lukasz   PropertyOverride(OperationMap&& operationMap,
203*43a095abSBorawski.Lukasz                    std::initializer_list<std::string>&& targets)
204*43a095abSBorawski.Lukasz       : EntityPrivilegesOverride(std::move(operationMap), std::move(targets)) {}
205*43a095abSBorawski.Lukasz };
206*43a095abSBorawski.Lukasz 
207*43a095abSBorawski.Lukasz class SubordinateOverride : public EntityPrivilegesOverride {
208*43a095abSBorawski.Lukasz  public:
209*43a095abSBorawski.Lukasz   SubordinateOverride(OperationMap&& operationMap,
210*43a095abSBorawski.Lukasz                       std::initializer_list<std::string>&& targets)
211*43a095abSBorawski.Lukasz       : EntityPrivilegesOverride(std::move(operationMap), std::move(targets)) {}
212*43a095abSBorawski.Lukasz };
213*43a095abSBorawski.Lukasz 
214*43a095abSBorawski.Lukasz class ResourceURIOverride : public EntityPrivilegesOverride {
215*43a095abSBorawski.Lukasz  public:
216*43a095abSBorawski.Lukasz   ResourceURIOverride(OperationMap&& operationMap,
217*43a095abSBorawski.Lukasz                       std::initializer_list<std::string>&& targets)
218*43a095abSBorawski.Lukasz       : EntityPrivilegesOverride(std::move(operationMap), std::move(targets)) {}
219*43a095abSBorawski.Lukasz };
220*43a095abSBorawski.Lukasz 
22186e1b661SBorawski.Lukasz /**
222aecb47a4SBorawski.Lukasz  * @brief  Class used to store privileges for Redfish entities
223*43a095abSBorawski.Lukasz  *
22486e1b661SBorawski.Lukasz  */
22586e1b661SBorawski.Lukasz class EntityPrivileges {
22686e1b661SBorawski.Lukasz  public:
227aecb47a4SBorawski.Lukasz   /**
228*43a095abSBorawski.Lukasz    * @brief Constructor for default case with no overrides
229*43a095abSBorawski.Lukasz    *
230*43a095abSBorawski.Lukasz    * @param[in] operationMap Operation map for the entity
231*43a095abSBorawski.Lukasz    *
232*43a095abSBorawski.Lukasz    */
233*43a095abSBorawski.Lukasz   EntityPrivileges(OperationMap&& operationMap)
234*43a095abSBorawski.Lukasz       : operationMap(std::move(operationMap)) {}
235*43a095abSBorawski.Lukasz 
236*43a095abSBorawski.Lukasz   /**
237*43a095abSBorawski.Lukasz    * @brief Constructors for overrides
238*43a095abSBorawski.Lukasz    *
239*43a095abSBorawski.Lukasz    * @param[in] operationMap         Default operation map for the entity
240*43a095abSBorawski.Lukasz    * @param[in] propertyOverrides    Vector of property overrides
241*43a095abSBorawski.Lukasz    * @param[in] subordinateOverrides Vector of subordinate overrides
242*43a095abSBorawski.Lukasz    * @param[in] resourceURIOverrides Vector of resource URI overrides
243*43a095abSBorawski.Lukasz    *
244*43a095abSBorawski.Lukasz    */
245*43a095abSBorawski.Lukasz   EntityPrivileges(OperationMap&& operationMap,
246*43a095abSBorawski.Lukasz                    std::vector<PropertyOverride>&& propertyOverrides,
247*43a095abSBorawski.Lukasz                    std::vector<SubordinateOverride>&& subordinateOverrides,
248*43a095abSBorawski.Lukasz                    std::vector<ResourceURIOverride>&& resourceURIOverrides)
249*43a095abSBorawski.Lukasz       : operationMap(std::move(operationMap)),
250*43a095abSBorawski.Lukasz         propertyOverrides(std::move(propertyOverrides)),
251*43a095abSBorawski.Lukasz         subordinateOverrides(std::move(subordinateOverrides)),
252*43a095abSBorawski.Lukasz         resourceURIOverrides(std::move(resourceURIOverrides)) {}
253*43a095abSBorawski.Lukasz 
254*43a095abSBorawski.Lukasz   /**
255aecb47a4SBorawski.Lukasz    * @brief Checks if a user is allowed to call an HTTP method
256aecb47a4SBorawski.Lukasz    *
257aecb47a4SBorawski.Lukasz    * @param[in] method       HTTP method
258aecb47a4SBorawski.Lukasz    * @param[in] user         Username
259aecb47a4SBorawski.Lukasz    *
260aecb47a4SBorawski.Lukasz    * @return                 True if method allowed, false otherwise
261*43a095abSBorawski.Lukasz    *
262aecb47a4SBorawski.Lukasz    */
263aecb47a4SBorawski.Lukasz   bool isMethodAllowedForUser(const crow::HTTPMethod method,
264*43a095abSBorawski.Lukasz                               const std::string& user) const {
265*43a095abSBorawski.Lukasz     // TODO: load user privileges from configuration as soon as its available
266*43a095abSBorawski.Lukasz     // now we are granting all privileges to everyone.
267*43a095abSBorawski.Lukasz     auto userPrivileges =
268*43a095abSBorawski.Lukasz         Privileges{"Login", "ConfigureManager", "ConfigureSelf",
269*43a095abSBorawski.Lukasz                    "ConfigureUsers", "ConfigureComponents"};
270*43a095abSBorawski.Lukasz 
271*43a095abSBorawski.Lukasz     return isMethodAllowedWithPrivileges(method, userPrivileges);
272*43a095abSBorawski.Lukasz   }
273aecb47a4SBorawski.Lukasz 
274aecb47a4SBorawski.Lukasz   /**
275aecb47a4SBorawski.Lukasz    * @brief Checks if given privileges allow to call an HTTP method
276aecb47a4SBorawski.Lukasz    *
277aecb47a4SBorawski.Lukasz    * @param[in] method       HTTP method
278aecb47a4SBorawski.Lukasz    * @param[in] user         Privileges
279aecb47a4SBorawski.Lukasz    *
280aecb47a4SBorawski.Lukasz    * @return                 True if method allowed, false otherwise
281*43a095abSBorawski.Lukasz    *
282aecb47a4SBorawski.Lukasz    */
283aecb47a4SBorawski.Lukasz   bool isMethodAllowedWithPrivileges(const crow::HTTPMethod method,
284*43a095abSBorawski.Lukasz                                      const Privileges& userPrivileges) const {
285*43a095abSBorawski.Lukasz     if (operationMap.find(method) == operationMap.end()) {
286*43a095abSBorawski.Lukasz       return false;
287*43a095abSBorawski.Lukasz     }
288aecb47a4SBorawski.Lukasz 
289*43a095abSBorawski.Lukasz     for (auto& requiredPrivileges : operationMap.at(method)) {
290*43a095abSBorawski.Lukasz       // Check if user has required base privileges
291*43a095abSBorawski.Lukasz       if (!verifyPrivileges(userPrivileges.getBasePrivilegeBitset(),
292*43a095abSBorawski.Lukasz                             requiredPrivileges.getBasePrivilegeBitset())) {
293*43a095abSBorawski.Lukasz         continue;
294*43a095abSBorawski.Lukasz       }
295*43a095abSBorawski.Lukasz 
296*43a095abSBorawski.Lukasz       // Check if user has required OEM privileges
297*43a095abSBorawski.Lukasz       if (!verifyPrivileges(userPrivileges.getOEMPrivilegeBitset(),
298*43a095abSBorawski.Lukasz                             requiredPrivileges.getOEMPrivilegeBitset())) {
299*43a095abSBorawski.Lukasz         continue;
300*43a095abSBorawski.Lukasz       }
301*43a095abSBorawski.Lukasz 
302*43a095abSBorawski.Lukasz       return true;
303*43a095abSBorawski.Lukasz     }
304*43a095abSBorawski.Lukasz     return false;
30586e1b661SBorawski.Lukasz   }
306aecb47a4SBorawski.Lukasz 
307aecb47a4SBorawski.Lukasz  private:
308aecb47a4SBorawski.Lukasz   bool verifyPrivileges(const privilegeBitset userPrivilegeBitset,
309*43a095abSBorawski.Lukasz                         const privilegeBitset requiredPrivilegeBitset) const {
310*43a095abSBorawski.Lukasz     return (userPrivilegeBitset & requiredPrivilegeBitset) ==
311*43a095abSBorawski.Lukasz            requiredPrivilegeBitset;
31286e1b661SBorawski.Lukasz   }
31386e1b661SBorawski.Lukasz 
314*43a095abSBorawski.Lukasz   OperationMap operationMap;
315aecb47a4SBorawski.Lukasz 
316*43a095abSBorawski.Lukasz   // Overrides are not implemented at the moment.
317*43a095abSBorawski.Lukasz   std::vector<PropertyOverride> propertyOverrides;
318*43a095abSBorawski.Lukasz   std::vector<SubordinateOverride> subordinateOverrides;
319*43a095abSBorawski.Lukasz   std::vector<ResourceURIOverride> resourceURIOverrides;
32086e1b661SBorawski.Lukasz };
32186e1b661SBorawski.Lukasz 
32286e1b661SBorawski.Lukasz }  // namespace redfish
32386e1b661SBorawski.Lukasz 
324