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