blob: 970946a60a4aa97f2548893ed141d72bc8c9e234 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
3// SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
Borawski.Lukasz86e1b662018-01-19 14:22:14 +01004#pragma once
5
Nan Zhoud5c80ad2022-07-11 01:16:31 +00006#include "logging.hpp"
Ninad Palsule3e72c202023-03-27 17:19:55 -05007#include "sessions.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +00008
Tanousf00032d2018-11-05 01:18:10 -03009#include <boost/beast/http/verb.hpp>
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010010#include <boost/container/flat_map.hpp>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000011#include <boost/container/vector.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050012
13#include <array>
14#include <bitset>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000015#include <cstddef>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000016#include <initializer_list>
17#include <string>
18#include <string_view>
19#include <utility>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020#include <vector>
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010021
Ed Tanous1abe55e2018-09-05 08:30:59 -070022namespace redfish
23{
24
25enum class PrivilegeType
26{
27 BASE,
28 OEM
29};
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010030
Ed Tanousa6927792018-03-06 10:01:57 -080031/** @brief A fixed array of compile time privileges */
Ed Tanousac25adb2024-04-13 11:57:35 -070032constexpr std::array<std::string_view, 5> basePrivileges{
Ed Tanous3ebd75f2018-03-05 18:20:01 -080033 "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
34 "ConfigureUsers"};
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010035
Ed Tanous271584a2019-07-09 16:24:22 -070036constexpr const size_t basePrivilegeCount = basePrivileges.size();
Ed Tanousa6927792018-03-06 10:01:57 -080037
38/** @brief Max number of privileges per type */
Ed Tanous271584a2019-07-09 16:24:22 -070039constexpr const size_t maxPrivilegeCount = 32;
Ed Tanousa6927792018-03-06 10:01:57 -080040
Ninad Palsule3e72c202023-03-27 17:19:55 -050041/**
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 */
Ed Tanousac25adb2024-04-13 11:57:35 -070047constexpr std::array<std::string_view, maxPrivilegeCount> privilegeNames{
Ninad Palsule3e72c202023-03-27 17:19:55 -050048 "Login", "ConfigureManager", "ConfigureComponents",
49 "ConfigureSelf", "ConfigureUsers", "OpenBMCHostConsole"};
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010050
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010051/**
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010052 * @brief Redfish privileges
53 *
Joseph Reynolds900f9492019-11-25 15:37:29 -060054 * This implements a set of Redfish privileges. These directly represent
55 * user privileges and help represent entity privileges.
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010056 *
Ed Tanous55c7b7a2018-05-22 15:27:24 -070057 * Each incoming Connection requires a comparison between privileges held
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010058 * 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 *
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010064 * A bit is set if the privilege is required (entity domain) or granted
65 * (user domain) and false otherwise.
66 *
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010067 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070068class Privileges
69{
70 public:
71 /**
72 * @brief Constructs object without any privileges active
73 *
74 */
75 Privileges() = default;
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010076
Ed Tanous1abe55e2018-09-05 08:30:59 -070077 /**
78 * @brief Constructs object with given privileges active
79 *
80 * @param[in] privilegeList List of privileges to be activated
81 *
82 */
83 Privileges(std::initializer_list<const char*> privilegeList)
84 {
85 for (const char* privilege : privilegeList)
86 {
87 if (!setSinglePrivilege(privilege))
88 {
Ed Tanousac25adb2024-04-13 11:57:35 -070089 BMCWEB_LOG_CRITICAL("Unable to set privilege {} in constructor",
Ed Tanous62598e32023-07-17 17:06:25 -070090 privilege);
Ed Tanous1abe55e2018-09-05 08:30:59 -070091 }
92 }
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010093 }
94
Ed Tanous1abe55e2018-09-05 08:30:59 -070095 /**
96 * @brief Sets given privilege in the bitset
97 *
98 * @param[in] privilege Privilege to be set
99 *
100 * @return None
101 *
102 */
Ed Tanous26ccae32023-02-16 10:28:44 -0800103 bool setSinglePrivilege(std::string_view privilege)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 {
Ed Tanous271584a2019-07-09 16:24:22 -0700105 for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 searchIndex++)
107 {
108 if (privilege == privilegeNames[searchIndex])
109 {
110 privilegeBitset.set(searchIndex);
111 return true;
112 }
113 }
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800114
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 return false;
Ed Tanousa6927792018-03-06 10:01:57 -0800116 }
117
Ed Tanous1abe55e2018-09-05 08:30:59 -0700118 /**
Joseph Reynolds900f9492019-11-25 15:37:29 -0600119 * @brief Resets the given privilege in the bitset
120 *
121 * @param[in] privilege Privilege to be reset
122 *
123 * @return None
124 *
125 */
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 /**
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141 * @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 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000149 std::vector<std::string>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700150 getActivePrivilegeNames(const PrivilegeType type) const
151 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000152 std::vector<std::string> activePrivileges;
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100153
Ed Tanous271584a2019-07-09 16:24:22 -0700154 size_t searchIndex = 0;
155 size_t endIndex = basePrivilegeCount;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700156 if (type == PrivilegeType::OEM)
157 {
Joseph Reynolds87704462021-08-24 14:42:39 -0500158 searchIndex = basePrivilegeCount;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 endIndex = privilegeNames.size();
160 }
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800161
Ed Tanous1abe55e2018-09-05 08:30:59 -0700162 for (; searchIndex < endIndex; searchIndex++)
163 {
164 if (privilegeBitset.test(searchIndex))
165 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000166 activePrivileges.emplace_back(privilegeNames[searchIndex]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700167 }
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 */
182 bool isSupersetOf(const Privileges& p) const
183 {
184 return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset;
185 }
186
Joseph Reynolds3bf4e632020-02-06 14:44:32 -0600187 /**
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 */
195 Privileges intersection(const Privileges& p) const
196 {
197 return Privileges{privilegeBitset & p.privilegeBitset};
198 }
199
Ed Tanous1abe55e2018-09-05 08:30:59 -0700200 private:
Ed Tanous4e23a442022-06-06 09:57:26 -0700201 explicit Privileges(const std::bitset<maxPrivilegeCount>& p) :
202 privilegeBitset{p}
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500203 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700204 std::bitset<maxPrivilegeCount> privilegeBitset = 0;
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100205};
206
Ninad Palsule3e72c202023-03-27 17:19:55 -0500207inline Privileges getUserPrivileges(const persistent_data::UserSession& session)
Ratan Gupta6f359562019-04-03 10:39:08 +0530208{
Ninad Palsule3e72c202023-03-27 17:19:55 -0500209 // default to no access
210 Privileges privs;
211
212 // Check if user is member of hostconsole group
213 for (const auto& userGroup : session.userGroups)
Ratan Gupta6f359562019-04-03 10:39:08 +0530214 {
Ninad Palsule3e72c202023-03-27 17:19:55 -0500215 if (userGroup == "hostconsole")
216 {
217 // Redfish privilege : host console access
218 privs.setSinglePrivilege("OpenBMCHostConsole");
219 break;
220 }
Ratan Gupta6f359562019-04-03 10:39:08 +0530221 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500222
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")
Ratan Gupta6f359562019-04-03 10:39:08 +0530233 {
234 // Redfish privilege : Operator
Ninad Palsule3e72c202023-03-27 17:19:55 -0500235 privs.setSinglePrivilege("Login");
236 privs.setSinglePrivilege("ConfigureSelf");
237 privs.setSinglePrivilege("ConfigureComponents");
Ratan Gupta6f359562019-04-03 10:39:08 +0530238 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500239 else if (session.userRole == "priv-user")
Ratan Gupta6f359562019-04-03 10:39:08 +0530240 {
241 // Redfish privilege : Readonly
Ninad Palsule3e72c202023-03-27 17:19:55 -0500242 privs.setSinglePrivilege("Login");
243 privs.setSinglePrivilege("ConfigureSelf");
Ratan Gupta6f359562019-04-03 10:39:08 +0530244 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500245
246 return privs;
Ratan Gupta6f359562019-04-03 10:39:08 +0530247}
248
Joseph Reynolds900f9492019-11-25 15:37:29 -0600249/**
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 **/
Ed Tanouse0d918b2018-03-27 17:41:04 -0700261using OperationMap = boost::container::flat_map<boost::beast::http::verb,
262 std::vector<Privileges>>;
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100263
Joseph Reynolds900f9492019-11-25 15:37:29 -0600264/* @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 */
271inline 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 }
Ed Tanous9eb808c2022-01-25 10:19:23 -0800280 for (const auto& requiredPrivileges : operationPrivilegesRequired)
Joseph Reynolds900f9492019-11-25 15:37:29 -0600281 {
Ed Tanous62598e32023-07-17 17:06:25 -0700282 BMCWEB_LOG_DEBUG("Checking operation privileges...");
Joseph Reynolds900f9492019-11-25 15:37:29 -0600283 if (userPrivileges.isSupersetOf(requiredPrivileges))
284 {
Ed Tanous62598e32023-07-17 17:06:25 -0700285 BMCWEB_LOG_DEBUG("...success");
Joseph Reynolds900f9492019-11-25 15:37:29 -0600286 return true;
287 }
288 }
289 return false;
290}
291
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100292/**
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800293 * @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
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100299 *
300 */
Ed Tanouse0d918b2018-03-27 17:41:04 -0700301inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method,
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800302 const OperationMap& operationMap,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700303 const Privileges& userPrivileges)
304{
305 const auto& it = operationMap.find(method);
306 if (it == operationMap.end())
307 {
308 return false;
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800309 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700310
Joseph Reynolds900f9492019-11-25 15:37:29 -0600311 return isOperationAllowedWithPrivileges(it->second, userPrivileges);
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800312}
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100313
Ed Tanous1abe55e2018-09-05 08:30:59 -0700314} // namespace redfish