blob: 5df4c31f55591ac06908251a58d32661b9f0b73a [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>
16#include <functional>
17#include <initializer_list>
18#include <string>
19#include <string_view>
20#include <utility>
Ed Tanous1abe55e2018-09-05 08:30:59 -070021#include <vector>
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010022
Ed Tanous1abe55e2018-09-05 08:30:59 -070023namespace redfish
24{
25
26enum class PrivilegeType
27{
28 BASE,
29 OEM
30};
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010031
Ed Tanousa6927792018-03-06 10:01:57 -080032/** @brief A fixed array of compile time privileges */
Ed Tanousac25adb2024-04-13 11:57:35 -070033constexpr std::array<std::string_view, 5> basePrivileges{
Ed Tanous3ebd75f2018-03-05 18:20:01 -080034 "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
35 "ConfigureUsers"};
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010036
Ed Tanous271584a2019-07-09 16:24:22 -070037constexpr const size_t basePrivilegeCount = basePrivileges.size();
Ed Tanousa6927792018-03-06 10:01:57 -080038
39/** @brief Max number of privileges per type */
Ed Tanous271584a2019-07-09 16:24:22 -070040constexpr const size_t maxPrivilegeCount = 32;
Ed Tanousa6927792018-03-06 10:01:57 -080041
Ninad Palsule3e72c202023-03-27 17:19:55 -050042/**
43 * @brief A vector of all privilege names and their indexes
44 * The privilege "OpenBMCHostConsole" is added to users who are members of the
45 * "hostconsole" user group. This privilege is required to access the host
46 * console.
47 */
Ed Tanousac25adb2024-04-13 11:57:35 -070048constexpr std::array<std::string_view, maxPrivilegeCount> privilegeNames{
Ninad Palsule3e72c202023-03-27 17:19:55 -050049 "Login", "ConfigureManager", "ConfigureComponents",
50 "ConfigureSelf", "ConfigureUsers", "OpenBMCHostConsole"};
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010051
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010052/**
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010053 * @brief Redfish privileges
54 *
Joseph Reynolds900f9492019-11-25 15:37:29 -060055 * This implements a set of Redfish privileges. These directly represent
56 * user privileges and help represent entity privileges.
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010057 *
Ed Tanous55c7b7a2018-05-22 15:27:24 -070058 * Each incoming Connection requires a comparison between privileges held
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010059 * by the user issuing a request and the target entity's privileges.
60 *
61 * To ensure best runtime performance of this comparison, privileges
62 * are represented as bitsets. Each bit in the bitset corresponds to a
63 * unique privilege name.
64 *
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010065 * A bit is set if the privilege is required (entity domain) or granted
66 * (user domain) and false otherwise.
67 *
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010068 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070069class Privileges
70{
71 public:
72 /**
73 * @brief Constructs object without any privileges active
74 *
75 */
76 Privileges() = default;
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010077
Ed Tanous1abe55e2018-09-05 08:30:59 -070078 /**
79 * @brief Constructs object with given privileges active
80 *
81 * @param[in] privilegeList List of privileges to be activated
82 *
83 */
84 Privileges(std::initializer_list<const char*> privilegeList)
85 {
86 for (const char* privilege : privilegeList)
87 {
88 if (!setSinglePrivilege(privilege))
89 {
Ed Tanousac25adb2024-04-13 11:57:35 -070090 BMCWEB_LOG_CRITICAL("Unable to set privilege {} in constructor",
Ed Tanous62598e32023-07-17 17:06:25 -070091 privilege);
Ed Tanous1abe55e2018-09-05 08:30:59 -070092 }
93 }
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010094 }
95
Ed Tanous1abe55e2018-09-05 08:30:59 -070096 /**
97 * @brief Sets given privilege in the bitset
98 *
99 * @param[in] privilege Privilege to be set
100 *
101 * @return None
102 *
103 */
Ed Tanous26ccae32023-02-16 10:28:44 -0800104 bool setSinglePrivilege(std::string_view privilege)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 {
Ed Tanous271584a2019-07-09 16:24:22 -0700106 for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 searchIndex++)
108 {
109 if (privilege == privilegeNames[searchIndex])
110 {
111 privilegeBitset.set(searchIndex);
112 return true;
113 }
114 }
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800115
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116 return false;
Ed Tanousa6927792018-03-06 10:01:57 -0800117 }
118
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119 /**
Joseph Reynolds900f9492019-11-25 15:37:29 -0600120 * @brief Resets the given privilege in the bitset
121 *
122 * @param[in] privilege Privilege to be reset
123 *
124 * @return None
125 *
126 */
127 bool resetSinglePrivilege(const char* privilege)
128 {
129 for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
130 searchIndex++)
131 {
132 if (privilege == privilegeNames[searchIndex])
133 {
134 privilegeBitset.reset(searchIndex);
135 return true;
136 }
137 }
138 return false;
139 }
140
141 /**
Ed Tanous1abe55e2018-09-05 08:30:59 -0700142 * @brief Retrieves names of all active privileges for a given type
143 *
144 * @param[in] type Base or OEM
145 *
146 * @return Vector of active privileges. Pointers are valid until
147 * the setSinglePrivilege is called, or the Privilege structure is destroyed
148 *
149 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000150 std::vector<std::string>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700151 getActivePrivilegeNames(const PrivilegeType type) const
152 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000153 std::vector<std::string> activePrivileges;
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100154
Ed Tanous271584a2019-07-09 16:24:22 -0700155 size_t searchIndex = 0;
156 size_t endIndex = basePrivilegeCount;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700157 if (type == PrivilegeType::OEM)
158 {
Joseph Reynolds87704462021-08-24 14:42:39 -0500159 searchIndex = basePrivilegeCount;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700160 endIndex = privilegeNames.size();
161 }
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800162
Ed Tanous1abe55e2018-09-05 08:30:59 -0700163 for (; searchIndex < endIndex; searchIndex++)
164 {
165 if (privilegeBitset.test(searchIndex))
166 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000167 activePrivileges.emplace_back(privilegeNames[searchIndex]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700168 }
169 }
170
171 return activePrivileges;
172 }
173
174 /**
175 * @brief Determines if this Privilege set is a superset of the given
176 * privilege set
177 *
178 * @param[in] privilege Privilege to be checked
179 *
180 * @return None
181 *
182 */
183 bool isSupersetOf(const Privileges& p) const
184 {
185 return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset;
186 }
187
Joseph Reynolds3bf4e632020-02-06 14:44:32 -0600188 /**
189 * @brief Returns the intersection of two Privilege sets.
190 *
191 * @param[in] privilege Privilege set to intersect with.
192 *
193 * @return The new Privilege set.
194 *
195 */
196 Privileges intersection(const Privileges& p) const
197 {
198 return Privileges{privilegeBitset & p.privilegeBitset};
199 }
200
Ed Tanous1abe55e2018-09-05 08:30:59 -0700201 private:
Ed Tanous4e23a442022-06-06 09:57:26 -0700202 explicit Privileges(const std::bitset<maxPrivilegeCount>& p) :
203 privilegeBitset{p}
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500204 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700205 std::bitset<maxPrivilegeCount> privilegeBitset = 0;
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100206};
207
Ninad Palsule3e72c202023-03-27 17:19:55 -0500208inline Privileges getUserPrivileges(const persistent_data::UserSession& session)
Ratan Gupta6f359562019-04-03 10:39:08 +0530209{
Ninad Palsule3e72c202023-03-27 17:19:55 -0500210 // default to no access
211 Privileges privs;
212
213 // Check if user is member of hostconsole group
214 for (const auto& userGroup : session.userGroups)
Ratan Gupta6f359562019-04-03 10:39:08 +0530215 {
Ninad Palsule3e72c202023-03-27 17:19:55 -0500216 if (userGroup == "hostconsole")
217 {
218 // Redfish privilege : host console access
219 privs.setSinglePrivilege("OpenBMCHostConsole");
220 break;
221 }
Ratan Gupta6f359562019-04-03 10:39:08 +0530222 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500223
224 if (session.userRole == "priv-admin")
225 {
226 // Redfish privilege : Administrator
227 privs.setSinglePrivilege("Login");
228 privs.setSinglePrivilege("ConfigureManager");
229 privs.setSinglePrivilege("ConfigureSelf");
230 privs.setSinglePrivilege("ConfigureUsers");
231 privs.setSinglePrivilege("ConfigureComponents");
232 }
233 else if (session.userRole == "priv-operator")
Ratan Gupta6f359562019-04-03 10:39:08 +0530234 {
235 // Redfish privilege : Operator
Ninad Palsule3e72c202023-03-27 17:19:55 -0500236 privs.setSinglePrivilege("Login");
237 privs.setSinglePrivilege("ConfigureSelf");
238 privs.setSinglePrivilege("ConfigureComponents");
Ratan Gupta6f359562019-04-03 10:39:08 +0530239 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500240 else if (session.userRole == "priv-user")
Ratan Gupta6f359562019-04-03 10:39:08 +0530241 {
242 // Redfish privilege : Readonly
Ninad Palsule3e72c202023-03-27 17:19:55 -0500243 privs.setSinglePrivilege("Login");
244 privs.setSinglePrivilege("ConfigureSelf");
Ratan Gupta6f359562019-04-03 10:39:08 +0530245 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500246
247 return privs;
Ratan Gupta6f359562019-04-03 10:39:08 +0530248}
249
Joseph Reynolds900f9492019-11-25 15:37:29 -0600250/**
251 * @brief The OperationMap represents the privileges required for a
252 * single entity (URI). It maps from the allowable verbs to the
253 * privileges required to use that operation.
254 *
255 * This represents the Redfish "Privilege AND and OR syntax" as given
256 * in the spec and shown in the Privilege Registry. This does not
257 * implement any Redfish property overrides, subordinate overrides, or
258 * resource URI overrides. This does not implement the limitation of
259 * the ConfigureSelf privilege to operate only on your own account or
260 * session.
261 **/
Ed Tanouse0d918b2018-03-27 17:41:04 -0700262using OperationMap = boost::container::flat_map<boost::beast::http::verb,
263 std::vector<Privileges>>;
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100264
Joseph Reynolds900f9492019-11-25 15:37:29 -0600265/* @brief Checks if user is allowed to call an operation
266 *
267 * @param[in] operationPrivilegesRequired Privileges required
268 * @param[in] userPrivileges Privileges the user has
269 *
270 * @return True if operation is allowed, false otherwise
271 */
272inline bool isOperationAllowedWithPrivileges(
273 const std::vector<Privileges>& operationPrivilegesRequired,
274 const Privileges& userPrivileges)
275{
276 // If there are no privileges assigned, there are no privileges required
277 if (operationPrivilegesRequired.empty())
278 {
279 return true;
280 }
Ed Tanous9eb808c2022-01-25 10:19:23 -0800281 for (const auto& requiredPrivileges : operationPrivilegesRequired)
Joseph Reynolds900f9492019-11-25 15:37:29 -0600282 {
Ed Tanous62598e32023-07-17 17:06:25 -0700283 BMCWEB_LOG_DEBUG("Checking operation privileges...");
Joseph Reynolds900f9492019-11-25 15:37:29 -0600284 if (userPrivileges.isSupersetOf(requiredPrivileges))
285 {
Ed Tanous62598e32023-07-17 17:06:25 -0700286 BMCWEB_LOG_DEBUG("...success");
Joseph Reynolds900f9492019-11-25 15:37:29 -0600287 return true;
288 }
289 }
290 return false;
291}
292
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100293/**
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800294 * @brief Checks if given privileges allow to call an HTTP method
295 *
296 * @param[in] method HTTP method
297 * @param[in] user Privileges
298 *
299 * @return True if method allowed, false otherwise
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100300 *
301 */
Ed Tanouse0d918b2018-03-27 17:41:04 -0700302inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method,
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800303 const OperationMap& operationMap,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700304 const Privileges& userPrivileges)
305{
306 const auto& it = operationMap.find(method);
307 if (it == operationMap.end())
308 {
309 return false;
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800310 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700311
Joseph Reynolds900f9492019-11-25 15:37:29 -0600312 return isOperationAllowedWithPrivileges(it->second, userPrivileges);
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800313}
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100314
Ed Tanous1abe55e2018-09-05 08:30:59 -0700315} // namespace redfish