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