xref: /openbmc/bmcweb/redfish-core/include/privileges.hpp (revision d78572018fc2022091ff8b8eb5a7fef2172ba3d6)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4 #pragma once
5 
6 #include "logging.hpp"
7 #include "sessions.hpp"
8 
9 #include <boost/beast/http/verb.hpp>
10 #include <boost/container/flat_map.hpp>
11 #include <boost/container/vector.hpp>
12 
13 #include <array>
14 #include <bitset>
15 #include <cstddef>
16 #include <initializer_list>
17 #include <string>
18 #include <string_view>
19 #include <utility>
20 #include <vector>
21 
22 namespace redfish
23 {
24 
25 enum class PrivilegeType
26 {
27     BASE,
28     OEM
29 };
30 
31 /** @brief A fixed array of compile time privileges  */
32 constexpr std::array<std::string_view, 5> basePrivileges{
33     "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
34     "ConfigureUsers"};
35 
36 constexpr const size_t basePrivilegeCount = basePrivileges.size();
37 
38 /** @brief Max number of privileges per type  */
39 constexpr const size_t maxPrivilegeCount = 32;
40 
41 /**
42  * @brief A vector of all privilege names and their indexes
43  * The privilege "OpenBMCHostConsole" is added to users who are members of the
44  * "hostconsole" user group. This privilege is required to access the host
45  * console.
46  */
47 constexpr std::array<std::string_view, maxPrivilegeCount> privilegeNames{
48     "Login",         "ConfigureManager", "ConfigureComponents",
49     "ConfigureSelf", "ConfigureUsers",   "OpenBMCHostConsole"};
50 
51 /**
52  * @brief Redfish privileges
53  *
54  *        This implements a set of Redfish privileges.  These directly represent
55  *        user privileges and help represent entity privileges.
56  *
57  *        Each incoming Connection requires a comparison between privileges held
58  *        by the user issuing a request and the target entity's privileges.
59  *
60  *        To ensure best runtime performance of this comparison, privileges
61  *        are represented as bitsets. Each bit in the bitset corresponds to a
62  *        unique privilege name.
63  *
64  *        A bit is set if the privilege is required (entity domain) or granted
65  *        (user domain) and false otherwise.
66  *
67  */
68 class Privileges
69 {
70   public:
71     /**
72      * @brief Constructs object without any privileges active
73      *
74      */
75     Privileges() = default;
76 
77     /**
78      * @brief Constructs object with given privileges active
79      *
80      * @param[in] privilegeList  List of privileges to be activated
81      *
82      */
Privileges(std::initializer_list<const char * > privilegeList)83     Privileges(std::initializer_list<const char*> privilegeList)
84     {
85         for (const char* privilege : privilegeList)
86         {
87             if (!setSinglePrivilege(privilege))
88             {
89                 BMCWEB_LOG_CRITICAL("Unable to set privilege {} in constructor",
90                                     privilege);
91             }
92         }
93     }
94 
95     /**
96      * @brief Sets given privilege in the bitset
97      *
98      * @param[in] privilege  Privilege to be set
99      *
100      * @return               None
101      *
102      */
setSinglePrivilege(std::string_view privilege)103     bool setSinglePrivilege(std::string_view privilege)
104     {
105         for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
106              searchIndex++)
107         {
108             if (privilege == privilegeNames[searchIndex])
109             {
110                 privilegeBitset.set(searchIndex);
111                 return true;
112             }
113         }
114 
115         return false;
116     }
117 
118     /**
119      * @brief Resets the given privilege in the bitset
120      *
121      * @param[in] privilege  Privilege to be reset
122      *
123      * @return               None
124      *
125      */
resetSinglePrivilege(const char * privilege)126     bool resetSinglePrivilege(const char* privilege)
127     {
128         for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
129              searchIndex++)
130         {
131             if (privilege == privilegeNames[searchIndex])
132             {
133                 privilegeBitset.reset(searchIndex);
134                 return true;
135             }
136         }
137         return false;
138     }
139 
140     /**
141      * @brief Retrieves names of all active privileges for a given type
142      *
143      * @param[in] type    Base or OEM
144      *
145      * @return            Vector of active privileges.  Pointers are valid until
146      * the setSinglePrivilege is called, or the Privilege structure is destroyed
147      *
148      */
149     std::vector<std::string>
getActivePrivilegeNames(const PrivilegeType type) const150         getActivePrivilegeNames(const PrivilegeType type) const
151     {
152         std::vector<std::string> activePrivileges;
153 
154         size_t searchIndex = 0;
155         size_t endIndex = basePrivilegeCount;
156         if (type == PrivilegeType::OEM)
157         {
158             searchIndex = basePrivilegeCount;
159             endIndex = privilegeNames.size();
160         }
161 
162         for (; searchIndex < endIndex; searchIndex++)
163         {
164             if (privilegeBitset.test(searchIndex))
165             {
166                 activePrivileges.emplace_back(privilegeNames[searchIndex]);
167             }
168         }
169 
170         return activePrivileges;
171     }
172 
173     /**
174      * @brief Determines if this Privilege set is a superset of the given
175      * privilege set
176      *
177      * @param[in] privilege  Privilege to be checked
178      *
179      * @return               None
180      *
181      */
isSupersetOf(const Privileges & p) const182     bool isSupersetOf(const Privileges& p) const
183     {
184         return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset;
185     }
186 
187     /**
188      * @brief Returns the intersection of two Privilege sets.
189      *
190      * @param[in] privilege  Privilege set to intersect with.
191      *
192      * @return               The new Privilege set.
193      *
194      */
intersection(const Privileges & p) const195     Privileges intersection(const Privileges& p) const
196     {
197         return Privileges{privilegeBitset & p.privilegeBitset};
198     }
199 
200   private:
Privileges(const std::bitset<maxPrivilegeCount> & p)201     explicit Privileges(const std::bitset<maxPrivilegeCount>& p) :
202         privilegeBitset{p}
203     {}
204     std::bitset<maxPrivilegeCount> privilegeBitset = 0;
205 };
206 
getUserPrivileges(const persistent_data::UserSession & session)207 inline Privileges getUserPrivileges(const persistent_data::UserSession& session)
208 {
209     // default to no access
210     Privileges privs;
211 
212     // Check if user is member of hostconsole group
213     for (const auto& userGroup : session.userGroups)
214     {
215         if (userGroup == "hostconsole")
216         {
217             // Redfish privilege : host console access
218             privs.setSinglePrivilege("OpenBMCHostConsole");
219             break;
220         }
221     }
222 
223     if (session.userRole == "priv-admin")
224     {
225         // Redfish privilege : Administrator
226         privs.setSinglePrivilege("Login");
227         privs.setSinglePrivilege("ConfigureManager");
228         privs.setSinglePrivilege("ConfigureSelf");
229         privs.setSinglePrivilege("ConfigureUsers");
230         privs.setSinglePrivilege("ConfigureComponents");
231     }
232     else if (session.userRole == "priv-operator")
233     {
234         // Redfish privilege : Operator
235         privs.setSinglePrivilege("Login");
236         privs.setSinglePrivilege("ConfigureSelf");
237         privs.setSinglePrivilege("ConfigureComponents");
238     }
239     else if (session.userRole == "priv-user")
240     {
241         // Redfish privilege : Readonly
242         privs.setSinglePrivilege("Login");
243         privs.setSinglePrivilege("ConfigureSelf");
244     }
245 
246     return privs;
247 }
248 
249 /**
250  * @brief The OperationMap represents the privileges required for a
251  * single entity (URI).  It maps from the allowable verbs to the
252  * privileges required to use that operation.
253  *
254  * This represents the Redfish "Privilege AND and OR syntax" as given
255  * in the spec and shown in the Privilege Registry.  This does not
256  * implement any Redfish property overrides, subordinate overrides, or
257  * resource URI overrides.  This does not implement the limitation of
258  * the ConfigureSelf privilege to operate only on your own account or
259  * session.
260  **/
261 using OperationMap = boost::container::flat_map<boost::beast::http::verb,
262                                                 std::vector<Privileges>>;
263 
264 /* @brief Checks if user is allowed to call an operation
265  *
266  * @param[in] operationPrivilegesRequired   Privileges required
267  * @param[in] userPrivileges                Privileges the user has
268  *
269  * @return                 True if operation is allowed, false otherwise
270  */
isOperationAllowedWithPrivileges(const std::vector<Privileges> & operationPrivilegesRequired,const Privileges & userPrivileges)271 inline bool isOperationAllowedWithPrivileges(
272     const std::vector<Privileges>& operationPrivilegesRequired,
273     const Privileges& userPrivileges)
274 {
275     // If there are no privileges assigned, there are no privileges required
276     if (operationPrivilegesRequired.empty())
277     {
278         return true;
279     }
280     for (const auto& requiredPrivileges : operationPrivilegesRequired)
281     {
282         BMCWEB_LOG_DEBUG("Checking operation privileges...");
283         if (userPrivileges.isSupersetOf(requiredPrivileges))
284         {
285             BMCWEB_LOG_DEBUG("...success");
286             return true;
287         }
288     }
289     return false;
290 }
291 
292 /**
293  * @brief Checks if given privileges allow to call an HTTP method
294  *
295  * @param[in] method       HTTP method
296  * @param[in] user         Privileges
297  *
298  * @return                 True if method allowed, false otherwise
299  *
300  */
isMethodAllowedWithPrivileges(const boost::beast::http::verb method,const OperationMap & operationMap,const Privileges & userPrivileges)301 inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method,
302                                           const OperationMap& operationMap,
303                                           const Privileges& userPrivileges)
304 {
305     const auto& it = operationMap.find(method);
306     if (it == operationMap.end())
307     {
308         return false;
309     }
310 
311     return isOperationAllowedWithPrivileges(it->second, userPrivileges);
312 }
313 
314 } // namespace redfish
315