blob: da1bf4be0043286d39210a3a35b4b19ee88108ea [file] [log] [blame]
Borawski.Lukasz86e1b662018-01-19 14:22:14 +01001/*
Ed Tanous6be832e2024-09-10 11:44:48 -07002Copyright (c) 2018 Intel Corporation
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010015*/
16#pragma once
17
Nan Zhoud5c80ad2022-07-11 01:16:31 +000018#include "logging.hpp"
Ninad Palsule3e72c202023-03-27 17:19:55 -050019#include "sessions.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +000020
Tanousf00032d2018-11-05 01:18:10 -030021#include <boost/beast/http/verb.hpp>
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010022#include <boost/container/flat_map.hpp>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000023#include <boost/container/vector.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050024
25#include <array>
26#include <bitset>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000027#include <cstddef>
28#include <functional>
29#include <initializer_list>
30#include <string>
31#include <string_view>
32#include <utility>
Ed Tanous1abe55e2018-09-05 08:30:59 -070033#include <vector>
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010034
Ed Tanous1abe55e2018-09-05 08:30:59 -070035namespace redfish
36{
37
38enum class PrivilegeType
39{
40 BASE,
41 OEM
42};
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010043
Ed Tanousa6927792018-03-06 10:01:57 -080044/** @brief A fixed array of compile time privileges */
Ed Tanousac25adb2024-04-13 11:57:35 -070045constexpr std::array<std::string_view, 5> basePrivileges{
Ed Tanous3ebd75f2018-03-05 18:20:01 -080046 "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
47 "ConfigureUsers"};
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010048
Ed Tanous271584a2019-07-09 16:24:22 -070049constexpr const size_t basePrivilegeCount = basePrivileges.size();
Ed Tanousa6927792018-03-06 10:01:57 -080050
51/** @brief Max number of privileges per type */
Ed Tanous271584a2019-07-09 16:24:22 -070052constexpr const size_t maxPrivilegeCount = 32;
Ed Tanousa6927792018-03-06 10:01:57 -080053
Ninad Palsule3e72c202023-03-27 17:19:55 -050054/**
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 */
Ed Tanousac25adb2024-04-13 11:57:35 -070060constexpr std::array<std::string_view, maxPrivilegeCount> privilegeNames{
Ninad Palsule3e72c202023-03-27 17:19:55 -050061 "Login", "ConfigureManager", "ConfigureComponents",
62 "ConfigureSelf", "ConfigureUsers", "OpenBMCHostConsole"};
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010063
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010064/**
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010065 * @brief Redfish privileges
66 *
Joseph Reynolds900f9492019-11-25 15:37:29 -060067 * This implements a set of Redfish privileges. These directly represent
68 * user privileges and help represent entity privileges.
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010069 *
Ed Tanous55c7b7a2018-05-22 15:27:24 -070070 * Each incoming Connection requires a comparison between privileges held
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010071 * 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 *
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010077 * A bit is set if the privilege is required (entity domain) or granted
78 * (user domain) and false otherwise.
79 *
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010080 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070081class Privileges
82{
83 public:
84 /**
85 * @brief Constructs object without any privileges active
86 *
87 */
88 Privileges() = default;
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010089
Ed Tanous1abe55e2018-09-05 08:30:59 -070090 /**
91 * @brief Constructs object with given privileges active
92 *
93 * @param[in] privilegeList List of privileges to be activated
94 *
95 */
96 Privileges(std::initializer_list<const char*> privilegeList)
97 {
98 for (const char* privilege : privilegeList)
99 {
100 if (!setSinglePrivilege(privilege))
101 {
Ed Tanousac25adb2024-04-13 11:57:35 -0700102 BMCWEB_LOG_CRITICAL("Unable to set privilege {} in constructor",
Ed Tanous62598e32023-07-17 17:06:25 -0700103 privilege);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 }
105 }
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100106 }
107
Ed Tanous1abe55e2018-09-05 08:30:59 -0700108 /**
109 * @brief Sets given privilege in the bitset
110 *
111 * @param[in] privilege Privilege to be set
112 *
113 * @return None
114 *
115 */
Ed Tanous26ccae32023-02-16 10:28:44 -0800116 bool setSinglePrivilege(std::string_view privilege)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700117 {
Ed Tanous271584a2019-07-09 16:24:22 -0700118 for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119 searchIndex++)
120 {
121 if (privilege == privilegeNames[searchIndex])
122 {
123 privilegeBitset.set(searchIndex);
124 return true;
125 }
126 }
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800127
Ed Tanous1abe55e2018-09-05 08:30:59 -0700128 return false;
Ed Tanousa6927792018-03-06 10:01:57 -0800129 }
130
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 /**
Joseph Reynolds900f9492019-11-25 15:37:29 -0600132 * @brief Resets the given privilege in the bitset
133 *
134 * @param[in] privilege Privilege to be reset
135 *
136 * @return None
137 *
138 */
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 /**
Ed Tanous1abe55e2018-09-05 08:30:59 -0700154 * @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 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000162 std::vector<std::string>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700163 getActivePrivilegeNames(const PrivilegeType type) const
164 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000165 std::vector<std::string> activePrivileges;
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100166
Ed Tanous271584a2019-07-09 16:24:22 -0700167 size_t searchIndex = 0;
168 size_t endIndex = basePrivilegeCount;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700169 if (type == PrivilegeType::OEM)
170 {
Joseph Reynolds87704462021-08-24 14:42:39 -0500171 searchIndex = basePrivilegeCount;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700172 endIndex = privilegeNames.size();
173 }
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800174
Ed Tanous1abe55e2018-09-05 08:30:59 -0700175 for (; searchIndex < endIndex; searchIndex++)
176 {
177 if (privilegeBitset.test(searchIndex))
178 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000179 activePrivileges.emplace_back(privilegeNames[searchIndex]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700180 }
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 */
195 bool isSupersetOf(const Privileges& p) const
196 {
197 return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset;
198 }
199
Joseph Reynolds3bf4e632020-02-06 14:44:32 -0600200 /**
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 */
208 Privileges intersection(const Privileges& p) const
209 {
210 return Privileges{privilegeBitset & p.privilegeBitset};
211 }
212
Ed Tanous1abe55e2018-09-05 08:30:59 -0700213 private:
Ed Tanous4e23a442022-06-06 09:57:26 -0700214 explicit Privileges(const std::bitset<maxPrivilegeCount>& p) :
215 privilegeBitset{p}
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500216 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700217 std::bitset<maxPrivilegeCount> privilegeBitset = 0;
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100218};
219
Ninad Palsule3e72c202023-03-27 17:19:55 -0500220inline Privileges getUserPrivileges(const persistent_data::UserSession& session)
Ratan Gupta6f359562019-04-03 10:39:08 +0530221{
Ninad Palsule3e72c202023-03-27 17:19:55 -0500222 // default to no access
223 Privileges privs;
224
225 // Check if user is member of hostconsole group
226 for (const auto& userGroup : session.userGroups)
Ratan Gupta6f359562019-04-03 10:39:08 +0530227 {
Ninad Palsule3e72c202023-03-27 17:19:55 -0500228 if (userGroup == "hostconsole")
229 {
230 // Redfish privilege : host console access
231 privs.setSinglePrivilege("OpenBMCHostConsole");
232 break;
233 }
Ratan Gupta6f359562019-04-03 10:39:08 +0530234 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500235
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")
Ratan Gupta6f359562019-04-03 10:39:08 +0530246 {
247 // Redfish privilege : Operator
Ninad Palsule3e72c202023-03-27 17:19:55 -0500248 privs.setSinglePrivilege("Login");
249 privs.setSinglePrivilege("ConfigureSelf");
250 privs.setSinglePrivilege("ConfigureComponents");
Ratan Gupta6f359562019-04-03 10:39:08 +0530251 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500252 else if (session.userRole == "priv-user")
Ratan Gupta6f359562019-04-03 10:39:08 +0530253 {
254 // Redfish privilege : Readonly
Ninad Palsule3e72c202023-03-27 17:19:55 -0500255 privs.setSinglePrivilege("Login");
256 privs.setSinglePrivilege("ConfigureSelf");
Ratan Gupta6f359562019-04-03 10:39:08 +0530257 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500258
259 return privs;
Ratan Gupta6f359562019-04-03 10:39:08 +0530260}
261
Joseph Reynolds900f9492019-11-25 15:37:29 -0600262/**
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 **/
Ed Tanouse0d918b2018-03-27 17:41:04 -0700274using OperationMap = boost::container::flat_map<boost::beast::http::verb,
275 std::vector<Privileges>>;
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100276
Joseph Reynolds900f9492019-11-25 15:37:29 -0600277/* @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 */
284inline 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 }
Ed Tanous9eb808c2022-01-25 10:19:23 -0800293 for (const auto& requiredPrivileges : operationPrivilegesRequired)
Joseph Reynolds900f9492019-11-25 15:37:29 -0600294 {
Ed Tanous62598e32023-07-17 17:06:25 -0700295 BMCWEB_LOG_DEBUG("Checking operation privileges...");
Joseph Reynolds900f9492019-11-25 15:37:29 -0600296 if (userPrivileges.isSupersetOf(requiredPrivileges))
297 {
Ed Tanous62598e32023-07-17 17:06:25 -0700298 BMCWEB_LOG_DEBUG("...success");
Joseph Reynolds900f9492019-11-25 15:37:29 -0600299 return true;
300 }
301 }
302 return false;
303}
304
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100305/**
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800306 * @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
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100312 *
313 */
Ed Tanouse0d918b2018-03-27 17:41:04 -0700314inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method,
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800315 const OperationMap& operationMap,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700316 const Privileges& userPrivileges)
317{
318 const auto& it = operationMap.find(method);
319 if (it == operationMap.end())
320 {
321 return false;
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800322 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700323
Joseph Reynolds900f9492019-11-25 15:37:29 -0600324 return isOperationAllowedWithPrivileges(it->second, userPrivileges);
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800325}
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100326
Ed Tanous1abe55e2018-09-05 08:30:59 -0700327} // namespace redfish