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