blob: bb2c2f3024855620b9b9e539379a5c27afecf483 [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>
24#include <boost/move/algo/move.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050025
26#include <array>
27#include <bitset>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000028#include <cstddef>
29#include <functional>
30#include <initializer_list>
31#include <string>
32#include <string_view>
33#include <utility>
Ed Tanous1abe55e2018-09-05 08:30:59 -070034#include <vector>
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010035
Nan Zhoud5c80ad2022-07-11 01:16:31 +000036// IWYU pragma: no_include <stddef.h>
37
Ed Tanous1abe55e2018-09-05 08:30:59 -070038namespace redfish
39{
40
41enum class PrivilegeType
42{
43 BASE,
44 OEM
45};
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010046
Ed Tanousa6927792018-03-06 10:01:57 -080047/** @brief A fixed array of compile time privileges */
48constexpr std::array<const char*, 5> basePrivileges{
Ed Tanous3ebd75f2018-03-05 18:20:01 -080049 "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
50 "ConfigureUsers"};
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010051
Ed Tanous271584a2019-07-09 16:24:22 -070052constexpr const size_t basePrivilegeCount = basePrivileges.size();
Ed Tanousa6927792018-03-06 10:01:57 -080053
54/** @brief Max number of privileges per type */
Ed Tanous271584a2019-07-09 16:24:22 -070055constexpr const size_t maxPrivilegeCount = 32;
Ed Tanousa6927792018-03-06 10:01:57 -080056
Ninad Palsule3e72c202023-03-27 17:19:55 -050057/**
58 * @brief A vector of all privilege names and their indexes
59 * The privilege "OpenBMCHostConsole" is added to users who are members of the
60 * "hostconsole" user group. This privilege is required to access the host
61 * console.
62 */
Ed Tanous23a21a12020-07-25 04:45:05 +000063static const std::array<std::string, maxPrivilegeCount> privilegeNames{
Ninad Palsule3e72c202023-03-27 17:19:55 -050064 "Login", "ConfigureManager", "ConfigureComponents",
65 "ConfigureSelf", "ConfigureUsers", "OpenBMCHostConsole"};
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010066
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010067/**
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010068 * @brief Redfish privileges
69 *
Joseph Reynolds900f9492019-11-25 15:37:29 -060070 * This implements a set of Redfish privileges. These directly represent
71 * user privileges and help represent entity privileges.
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010072 *
Ed Tanous55c7b7a2018-05-22 15:27:24 -070073 * Each incoming Connection requires a comparison between privileges held
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010074 * by the user issuing a request and the target entity's privileges.
75 *
76 * To ensure best runtime performance of this comparison, privileges
77 * are represented as bitsets. Each bit in the bitset corresponds to a
78 * unique privilege name.
79 *
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010080 * A bit is set if the privilege is required (entity domain) or granted
81 * (user domain) and false otherwise.
82 *
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010083 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070084class Privileges
85{
86 public:
87 /**
88 * @brief Constructs object without any privileges active
89 *
90 */
91 Privileges() = default;
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010092
Ed Tanous1abe55e2018-09-05 08:30:59 -070093 /**
94 * @brief Constructs object with given privileges active
95 *
96 * @param[in] privilegeList List of privileges to be activated
97 *
98 */
99 Privileges(std::initializer_list<const char*> privilegeList)
100 {
101 for (const char* privilege : privilegeList)
102 {
103 if (!setSinglePrivilege(privilege))
104 {
105 BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege
106 << "in constructor";
107 }
108 }
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100109 }
110
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111 /**
112 * @brief Sets given privilege in the bitset
113 *
114 * @param[in] privilege Privilege to be set
115 *
116 * @return None
117 *
118 */
Ed Tanous26ccae32023-02-16 10:28:44 -0800119 bool setSinglePrivilege(std::string_view privilege)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 {
Ed Tanous271584a2019-07-09 16:24:22 -0700121 for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700122 searchIndex++)
123 {
124 if (privilege == privilegeNames[searchIndex])
125 {
126 privilegeBitset.set(searchIndex);
127 return true;
128 }
129 }
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800130
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 return false;
Ed Tanousa6927792018-03-06 10:01:57 -0800132 }
133
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134 /**
Joseph Reynolds900f9492019-11-25 15:37:29 -0600135 * @brief Resets the given privilege in the bitset
136 *
137 * @param[in] privilege Privilege to be reset
138 *
139 * @return None
140 *
141 */
142 bool resetSinglePrivilege(const char* privilege)
143 {
144 for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
145 searchIndex++)
146 {
147 if (privilege == privilegeNames[searchIndex])
148 {
149 privilegeBitset.reset(searchIndex);
150 return true;
151 }
152 }
153 return false;
154 }
155
156 /**
Ed Tanous1abe55e2018-09-05 08:30:59 -0700157 * @brief Retrieves names of all active privileges for a given type
158 *
159 * @param[in] type Base or OEM
160 *
161 * @return Vector of active privileges. Pointers are valid until
162 * the setSinglePrivilege is called, or the Privilege structure is destroyed
163 *
164 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000165 std::vector<std::string>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700166 getActivePrivilegeNames(const PrivilegeType type) const
167 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000168 std::vector<std::string> activePrivileges;
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100169
Ed Tanous271584a2019-07-09 16:24:22 -0700170 size_t searchIndex = 0;
171 size_t endIndex = basePrivilegeCount;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700172 if (type == PrivilegeType::OEM)
173 {
Joseph Reynolds87704462021-08-24 14:42:39 -0500174 searchIndex = basePrivilegeCount;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700175 endIndex = privilegeNames.size();
176 }
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800177
Ed Tanous1abe55e2018-09-05 08:30:59 -0700178 for (; searchIndex < endIndex; searchIndex++)
179 {
180 if (privilegeBitset.test(searchIndex))
181 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000182 activePrivileges.emplace_back(privilegeNames[searchIndex]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700183 }
184 }
185
186 return activePrivileges;
187 }
188
189 /**
190 * @brief Determines if this Privilege set is a superset of the given
191 * privilege set
192 *
193 * @param[in] privilege Privilege to be checked
194 *
195 * @return None
196 *
197 */
198 bool isSupersetOf(const Privileges& p) const
199 {
200 return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset;
201 }
202
Joseph Reynolds3bf4e632020-02-06 14:44:32 -0600203 /**
204 * @brief Returns the intersection of two Privilege sets.
205 *
206 * @param[in] privilege Privilege set to intersect with.
207 *
208 * @return The new Privilege set.
209 *
210 */
211 Privileges intersection(const Privileges& p) const
212 {
213 return Privileges{privilegeBitset & p.privilegeBitset};
214 }
215
Ed Tanous1abe55e2018-09-05 08:30:59 -0700216 private:
Ed Tanous4e23a442022-06-06 09:57:26 -0700217 explicit Privileges(const std::bitset<maxPrivilegeCount>& p) :
218 privilegeBitset{p}
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500219 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700220 std::bitset<maxPrivilegeCount> privilegeBitset = 0;
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100221};
222
Ninad Palsule3e72c202023-03-27 17:19:55 -0500223inline Privileges getUserPrivileges(const persistent_data::UserSession& session)
Ratan Gupta6f359562019-04-03 10:39:08 +0530224{
Ninad Palsule3e72c202023-03-27 17:19:55 -0500225 // default to no access
226 Privileges privs;
227
228 // Check if user is member of hostconsole group
229 for (const auto& userGroup : session.userGroups)
Ratan Gupta6f359562019-04-03 10:39:08 +0530230 {
Ninad Palsule3e72c202023-03-27 17:19:55 -0500231 if (userGroup == "hostconsole")
232 {
233 // Redfish privilege : host console access
234 privs.setSinglePrivilege("OpenBMCHostConsole");
235 break;
236 }
Ratan Gupta6f359562019-04-03 10:39:08 +0530237 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500238
239 if (session.userRole == "priv-admin")
240 {
241 // Redfish privilege : Administrator
242 privs.setSinglePrivilege("Login");
243 privs.setSinglePrivilege("ConfigureManager");
244 privs.setSinglePrivilege("ConfigureSelf");
245 privs.setSinglePrivilege("ConfigureUsers");
246 privs.setSinglePrivilege("ConfigureComponents");
247 }
248 else if (session.userRole == "priv-operator")
Ratan Gupta6f359562019-04-03 10:39:08 +0530249 {
250 // Redfish privilege : Operator
Ninad Palsule3e72c202023-03-27 17:19:55 -0500251 privs.setSinglePrivilege("Login");
252 privs.setSinglePrivilege("ConfigureSelf");
253 privs.setSinglePrivilege("ConfigureComponents");
Ratan Gupta6f359562019-04-03 10:39:08 +0530254 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500255 else if (session.userRole == "priv-user")
Ratan Gupta6f359562019-04-03 10:39:08 +0530256 {
257 // Redfish privilege : Readonly
Ninad Palsule3e72c202023-03-27 17:19:55 -0500258 privs.setSinglePrivilege("Login");
259 privs.setSinglePrivilege("ConfigureSelf");
Ratan Gupta6f359562019-04-03 10:39:08 +0530260 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500261
262 return privs;
Ratan Gupta6f359562019-04-03 10:39:08 +0530263}
264
Joseph Reynolds900f9492019-11-25 15:37:29 -0600265/**
266 * @brief The OperationMap represents the privileges required for a
267 * single entity (URI). It maps from the allowable verbs to the
268 * privileges required to use that operation.
269 *
270 * This represents the Redfish "Privilege AND and OR syntax" as given
271 * in the spec and shown in the Privilege Registry. This does not
272 * implement any Redfish property overrides, subordinate overrides, or
273 * resource URI overrides. This does not implement the limitation of
274 * the ConfigureSelf privilege to operate only on your own account or
275 * session.
276 **/
Ed Tanouse0d918b2018-03-27 17:41:04 -0700277using OperationMap = boost::container::flat_map<boost::beast::http::verb,
278 std::vector<Privileges>>;
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100279
Joseph Reynolds900f9492019-11-25 15:37:29 -0600280/* @brief Checks if user is allowed to call an operation
281 *
282 * @param[in] operationPrivilegesRequired Privileges required
283 * @param[in] userPrivileges Privileges the user has
284 *
285 * @return True if operation is allowed, false otherwise
286 */
287inline bool isOperationAllowedWithPrivileges(
288 const std::vector<Privileges>& operationPrivilegesRequired,
289 const Privileges& userPrivileges)
290{
291 // If there are no privileges assigned, there are no privileges required
292 if (operationPrivilegesRequired.empty())
293 {
294 return true;
295 }
Ed Tanous9eb808c2022-01-25 10:19:23 -0800296 for (const auto& requiredPrivileges : operationPrivilegesRequired)
Joseph Reynolds900f9492019-11-25 15:37:29 -0600297 {
Andrew Geissler54fbf172021-11-11 15:59:30 -0600298 BMCWEB_LOG_DEBUG << "Checking operation privileges...";
Joseph Reynolds900f9492019-11-25 15:37:29 -0600299 if (userPrivileges.isSupersetOf(requiredPrivileges))
300 {
Andrew Geissler54fbf172021-11-11 15:59:30 -0600301 BMCWEB_LOG_DEBUG << "...success";
Joseph Reynolds900f9492019-11-25 15:37:29 -0600302 return true;
303 }
304 }
305 return false;
306}
307
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100308/**
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800309 * @brief Checks if given privileges allow to call an HTTP method
310 *
311 * @param[in] method HTTP method
312 * @param[in] user Privileges
313 *
314 * @return True if method allowed, false otherwise
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100315 *
316 */
Ed Tanouse0d918b2018-03-27 17:41:04 -0700317inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method,
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800318 const OperationMap& operationMap,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700319 const Privileges& userPrivileges)
320{
321 const auto& it = operationMap.find(method);
322 if (it == operationMap.end())
323 {
324 return false;
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800325 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700326
Joseph Reynolds900f9492019-11-25 15:37:29 -0600327 return isOperationAllowedWithPrivileges(it->second, userPrivileges);
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800328}
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100329
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330} // namespace redfish