blob: 87780ee84f6a816b78886c9dec96d02e2d154638 [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
Nan Zhoud5c80ad2022-07-11 01:16:31 +000035// IWYU pragma: no_include <stddef.h>
36
Ed Tanous1abe55e2018-09-05 08:30:59 -070037namespace redfish
38{
39
40enum class PrivilegeType
41{
42 BASE,
43 OEM
44};
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010045
Ed Tanousa6927792018-03-06 10:01:57 -080046/** @brief A fixed array of compile time privileges */
Ed Tanousac25adb2024-04-13 11:57:35 -070047constexpr std::array<std::string_view, 5> basePrivileges{
Ed Tanous3ebd75f2018-03-05 18:20:01 -080048 "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
49 "ConfigureUsers"};
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010050
Ed Tanous271584a2019-07-09 16:24:22 -070051constexpr const size_t basePrivilegeCount = basePrivileges.size();
Ed Tanousa6927792018-03-06 10:01:57 -080052
53/** @brief Max number of privileges per type */
Ed Tanous271584a2019-07-09 16:24:22 -070054constexpr const size_t maxPrivilegeCount = 32;
Ed Tanousa6927792018-03-06 10:01:57 -080055
Ninad Palsule3e72c202023-03-27 17:19:55 -050056/**
57 * @brief A vector of all privilege names and their indexes
58 * The privilege "OpenBMCHostConsole" is added to users who are members of the
59 * "hostconsole" user group. This privilege is required to access the host
60 * console.
61 */
Ed Tanousac25adb2024-04-13 11:57:35 -070062constexpr std::array<std::string_view, maxPrivilegeCount> privilegeNames{
Ninad Palsule3e72c202023-03-27 17:19:55 -050063 "Login", "ConfigureManager", "ConfigureComponents",
64 "ConfigureSelf", "ConfigureUsers", "OpenBMCHostConsole"};
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010065
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010066/**
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010067 * @brief Redfish privileges
68 *
Joseph Reynolds900f9492019-11-25 15:37:29 -060069 * This implements a set of Redfish privileges. These directly represent
70 * user privileges and help represent entity privileges.
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010071 *
Ed Tanous55c7b7a2018-05-22 15:27:24 -070072 * Each incoming Connection requires a comparison between privileges held
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010073 * by the user issuing a request and the target entity's privileges.
74 *
75 * To ensure best runtime performance of this comparison, privileges
76 * are represented as bitsets. Each bit in the bitset corresponds to a
77 * unique privilege name.
78 *
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010079 * A bit is set if the privilege is required (entity domain) or granted
80 * (user domain) and false otherwise.
81 *
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010082 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070083class Privileges
84{
85 public:
86 /**
87 * @brief Constructs object without any privileges active
88 *
89 */
90 Privileges() = default;
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010091
Ed Tanous1abe55e2018-09-05 08:30:59 -070092 /**
93 * @brief Constructs object with given privileges active
94 *
95 * @param[in] privilegeList List of privileges to be activated
96 *
97 */
98 Privileges(std::initializer_list<const char*> privilegeList)
99 {
100 for (const char* privilege : privilegeList)
101 {
102 if (!setSinglePrivilege(privilege))
103 {
Ed Tanousac25adb2024-04-13 11:57:35 -0700104 BMCWEB_LOG_CRITICAL("Unable to set privilege {} in constructor",
Ed Tanous62598e32023-07-17 17:06:25 -0700105 privilege);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 }
107 }
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100108 }
109
Ed Tanous1abe55e2018-09-05 08:30:59 -0700110 /**
111 * @brief Sets given privilege in the bitset
112 *
113 * @param[in] privilege Privilege to be set
114 *
115 * @return None
116 *
117 */
Ed Tanous26ccae32023-02-16 10:28:44 -0800118 bool setSinglePrivilege(std::string_view privilege)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119 {
Ed Tanous271584a2019-07-09 16:24:22 -0700120 for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 searchIndex++)
122 {
123 if (privilege == privilegeNames[searchIndex])
124 {
125 privilegeBitset.set(searchIndex);
126 return true;
127 }
128 }
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800129
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130 return false;
Ed Tanousa6927792018-03-06 10:01:57 -0800131 }
132
Ed Tanous1abe55e2018-09-05 08:30:59 -0700133 /**
Joseph Reynolds900f9492019-11-25 15:37:29 -0600134 * @brief Resets the given privilege in the bitset
135 *
136 * @param[in] privilege Privilege to be reset
137 *
138 * @return None
139 *
140 */
141 bool resetSinglePrivilege(const char* privilege)
142 {
143 for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
144 searchIndex++)
145 {
146 if (privilege == privilegeNames[searchIndex])
147 {
148 privilegeBitset.reset(searchIndex);
149 return true;
150 }
151 }
152 return false;
153 }
154
155 /**
Ed Tanous1abe55e2018-09-05 08:30:59 -0700156 * @brief Retrieves names of all active privileges for a given type
157 *
158 * @param[in] type Base or OEM
159 *
160 * @return Vector of active privileges. Pointers are valid until
161 * the setSinglePrivilege is called, or the Privilege structure is destroyed
162 *
163 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000164 std::vector<std::string>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700165 getActivePrivilegeNames(const PrivilegeType type) const
166 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000167 std::vector<std::string> activePrivileges;
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100168
Ed Tanous271584a2019-07-09 16:24:22 -0700169 size_t searchIndex = 0;
170 size_t endIndex = basePrivilegeCount;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700171 if (type == PrivilegeType::OEM)
172 {
Joseph Reynolds87704462021-08-24 14:42:39 -0500173 searchIndex = basePrivilegeCount;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700174 endIndex = privilegeNames.size();
175 }
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800176
Ed Tanous1abe55e2018-09-05 08:30:59 -0700177 for (; searchIndex < endIndex; searchIndex++)
178 {
179 if (privilegeBitset.test(searchIndex))
180 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000181 activePrivileges.emplace_back(privilegeNames[searchIndex]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700182 }
183 }
184
185 return activePrivileges;
186 }
187
188 /**
189 * @brief Determines if this Privilege set is a superset of the given
190 * privilege set
191 *
192 * @param[in] privilege Privilege to be checked
193 *
194 * @return None
195 *
196 */
197 bool isSupersetOf(const Privileges& p) const
198 {
199 return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset;
200 }
201
Joseph Reynolds3bf4e632020-02-06 14:44:32 -0600202 /**
203 * @brief Returns the intersection of two Privilege sets.
204 *
205 * @param[in] privilege Privilege set to intersect with.
206 *
207 * @return The new Privilege set.
208 *
209 */
210 Privileges intersection(const Privileges& p) const
211 {
212 return Privileges{privilegeBitset & p.privilegeBitset};
213 }
214
Ed Tanous1abe55e2018-09-05 08:30:59 -0700215 private:
Ed Tanous4e23a442022-06-06 09:57:26 -0700216 explicit Privileges(const std::bitset<maxPrivilegeCount>& p) :
217 privilegeBitset{p}
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500218 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700219 std::bitset<maxPrivilegeCount> privilegeBitset = 0;
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100220};
221
Ninad Palsule3e72c202023-03-27 17:19:55 -0500222inline Privileges getUserPrivileges(const persistent_data::UserSession& session)
Ratan Gupta6f359562019-04-03 10:39:08 +0530223{
Ninad Palsule3e72c202023-03-27 17:19:55 -0500224 // default to no access
225 Privileges privs;
226
227 // Check if user is member of hostconsole group
228 for (const auto& userGroup : session.userGroups)
Ratan Gupta6f359562019-04-03 10:39:08 +0530229 {
Ninad Palsule3e72c202023-03-27 17:19:55 -0500230 if (userGroup == "hostconsole")
231 {
232 // Redfish privilege : host console access
233 privs.setSinglePrivilege("OpenBMCHostConsole");
234 break;
235 }
Ratan Gupta6f359562019-04-03 10:39:08 +0530236 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500237
238 if (session.userRole == "priv-admin")
239 {
240 // Redfish privilege : Administrator
241 privs.setSinglePrivilege("Login");
242 privs.setSinglePrivilege("ConfigureManager");
243 privs.setSinglePrivilege("ConfigureSelf");
244 privs.setSinglePrivilege("ConfigureUsers");
245 privs.setSinglePrivilege("ConfigureComponents");
246 }
247 else if (session.userRole == "priv-operator")
Ratan Gupta6f359562019-04-03 10:39:08 +0530248 {
249 // Redfish privilege : Operator
Ninad Palsule3e72c202023-03-27 17:19:55 -0500250 privs.setSinglePrivilege("Login");
251 privs.setSinglePrivilege("ConfigureSelf");
252 privs.setSinglePrivilege("ConfigureComponents");
Ratan Gupta6f359562019-04-03 10:39:08 +0530253 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500254 else if (session.userRole == "priv-user")
Ratan Gupta6f359562019-04-03 10:39:08 +0530255 {
256 // Redfish privilege : Readonly
Ninad Palsule3e72c202023-03-27 17:19:55 -0500257 privs.setSinglePrivilege("Login");
258 privs.setSinglePrivilege("ConfigureSelf");
Ratan Gupta6f359562019-04-03 10:39:08 +0530259 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500260
261 return privs;
Ratan Gupta6f359562019-04-03 10:39:08 +0530262}
263
Joseph Reynolds900f9492019-11-25 15:37:29 -0600264/**
265 * @brief The OperationMap represents the privileges required for a
266 * single entity (URI). It maps from the allowable verbs to the
267 * privileges required to use that operation.
268 *
269 * This represents the Redfish "Privilege AND and OR syntax" as given
270 * in the spec and shown in the Privilege Registry. This does not
271 * implement any Redfish property overrides, subordinate overrides, or
272 * resource URI overrides. This does not implement the limitation of
273 * the ConfigureSelf privilege to operate only on your own account or
274 * session.
275 **/
Ed Tanouse0d918b2018-03-27 17:41:04 -0700276using OperationMap = boost::container::flat_map<boost::beast::http::verb,
277 std::vector<Privileges>>;
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100278
Joseph Reynolds900f9492019-11-25 15:37:29 -0600279/* @brief Checks if user is allowed to call an operation
280 *
281 * @param[in] operationPrivilegesRequired Privileges required
282 * @param[in] userPrivileges Privileges the user has
283 *
284 * @return True if operation is allowed, false otherwise
285 */
286inline bool isOperationAllowedWithPrivileges(
287 const std::vector<Privileges>& operationPrivilegesRequired,
288 const Privileges& userPrivileges)
289{
290 // If there are no privileges assigned, there are no privileges required
291 if (operationPrivilegesRequired.empty())
292 {
293 return true;
294 }
Ed Tanous9eb808c2022-01-25 10:19:23 -0800295 for (const auto& requiredPrivileges : operationPrivilegesRequired)
Joseph Reynolds900f9492019-11-25 15:37:29 -0600296 {
Ed Tanous62598e32023-07-17 17:06:25 -0700297 BMCWEB_LOG_DEBUG("Checking operation privileges...");
Joseph Reynolds900f9492019-11-25 15:37:29 -0600298 if (userPrivileges.isSupersetOf(requiredPrivileges))
299 {
Ed Tanous62598e32023-07-17 17:06:25 -0700300 BMCWEB_LOG_DEBUG("...success");
Joseph Reynolds900f9492019-11-25 15:37:29 -0600301 return true;
302 }
303 }
304 return false;
305}
306
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100307/**
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800308 * @brief Checks if given privileges allow to call an HTTP method
309 *
310 * @param[in] method HTTP method
311 * @param[in] user Privileges
312 *
313 * @return True if method allowed, false otherwise
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100314 *
315 */
Ed Tanouse0d918b2018-03-27 17:41:04 -0700316inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method,
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800317 const OperationMap& operationMap,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700318 const Privileges& userPrivileges)
319{
320 const auto& it = operationMap.find(method);
321 if (it == operationMap.end())
322 {
323 return false;
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800324 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700325
Joseph Reynolds900f9492019-11-25 15:37:29 -0600326 return isOperationAllowedWithPrivileges(it->second, userPrivileges);
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800327}
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100328
Ed Tanous1abe55e2018-09-05 08:30:59 -0700329} // namespace redfish