blob: 8a4fd771a224c7508622b2d7e4f9e56293bfdc70 [file] [log] [blame]
Borawski.Lukasz86e1b662018-01-19 14:22:14 +01001/*
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
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