blob: f20cedee157fec9c0817f317ce4342b85e1de6e3 [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
Tanousf00032d2018-11-05 01:18:10 -030018#include <boost/beast/http/verb.hpp>
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010019#include <boost/container/flat_map.hpp>
Ed Tanous04e438c2020-10-03 08:06:26 -070020#include <logging.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050021
22#include <array>
23#include <bitset>
Ed Tanous1abe55e2018-09-05 08:30:59 -070024#include <cstdint>
25#include <vector>
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace redfish
28{
29
30enum class PrivilegeType
31{
32 BASE,
33 OEM
34};
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010035
Ed Tanousa6927792018-03-06 10:01:57 -080036/** @brief A fixed array of compile time privileges */
37constexpr std::array<const char*, 5> basePrivileges{
Ed Tanous3ebd75f2018-03-05 18:20:01 -080038 "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
39 "ConfigureUsers"};
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010040
Ed Tanous271584a2019-07-09 16:24:22 -070041constexpr const size_t basePrivilegeCount = basePrivileges.size();
Ed Tanousa6927792018-03-06 10:01:57 -080042
43/** @brief Max number of privileges per type */
Ed Tanous271584a2019-07-09 16:24:22 -070044constexpr const size_t maxPrivilegeCount = 32;
Ed Tanousa6927792018-03-06 10:01:57 -080045
46/** @brief A vector of all privilege names and their indexes */
Ed Tanous23a21a12020-07-25 04:45:05 +000047static const std::array<std::string, maxPrivilegeCount> privilegeNames{
48 "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
49 "ConfigureUsers"};
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010050
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010051/**
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010052 * @brief Redfish privileges
53 *
Joseph Reynolds900f9492019-11-25 15:37:29 -060054 * This implements a set of Redfish privileges. These directly represent
55 * user privileges and help represent entity privileges.
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010056 *
Ed Tanous55c7b7a2018-05-22 15:27:24 -070057 * Each incoming Connection requires a comparison between privileges held
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010058 * by the user issuing a request and the target entity's privileges.
59 *
60 * To ensure best runtime performance of this comparison, privileges
61 * are represented as bitsets. Each bit in the bitset corresponds to a
62 * unique privilege name.
63 *
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010064 * A bit is set if the privilege is required (entity domain) or granted
65 * (user domain) and false otherwise.
66 *
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010067 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070068class Privileges
69{
70 public:
71 /**
72 * @brief Constructs object without any privileges active
73 *
74 */
75 Privileges() = default;
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010076
Ed Tanous1abe55e2018-09-05 08:30:59 -070077 /**
78 * @brief Constructs object with given privileges active
79 *
80 * @param[in] privilegeList List of privileges to be activated
81 *
82 */
83 Privileges(std::initializer_list<const char*> privilegeList)
84 {
85 for (const char* privilege : privilegeList)
86 {
87 if (!setSinglePrivilege(privilege))
88 {
89 BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege
90 << "in constructor";
91 }
92 }
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010093 }
94
Ed Tanous1abe55e2018-09-05 08:30:59 -070095 /**
96 * @brief Sets given privilege in the bitset
97 *
98 * @param[in] privilege Privilege to be set
99 *
100 * @return None
101 *
102 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000103 bool setSinglePrivilege(const std::string_view privilege)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 {
Ed Tanous271584a2019-07-09 16:24:22 -0700105 for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 searchIndex++)
107 {
108 if (privilege == privilegeNames[searchIndex])
109 {
110 privilegeBitset.set(searchIndex);
111 return true;
112 }
113 }
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800114
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 return false;
Ed Tanousa6927792018-03-06 10:01:57 -0800116 }
117
Ed Tanous1abe55e2018-09-05 08:30:59 -0700118 /**
Joseph Reynolds900f9492019-11-25 15:37:29 -0600119 * @brief Resets the given privilege in the bitset
120 *
121 * @param[in] privilege Privilege to be reset
122 *
123 * @return None
124 *
125 */
126 bool resetSinglePrivilege(const char* privilege)
127 {
128 for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
129 searchIndex++)
130 {
131 if (privilege == privilegeNames[searchIndex])
132 {
133 privilegeBitset.reset(searchIndex);
134 return true;
135 }
136 }
137 return false;
138 }
139
140 /**
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141 * @brief Retrieves names of all active privileges for a given type
142 *
143 * @param[in] type Base or OEM
144 *
145 * @return Vector of active privileges. Pointers are valid until
146 * the setSinglePrivilege is called, or the Privilege structure is destroyed
147 *
148 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000149 std::vector<std::string>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700150 getActivePrivilegeNames(const PrivilegeType type) const
151 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000152 std::vector<std::string> activePrivileges;
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100153
Ed Tanous271584a2019-07-09 16:24:22 -0700154 size_t searchIndex = 0;
155 size_t endIndex = basePrivilegeCount;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700156 if (type == PrivilegeType::OEM)
157 {
Joseph Reynolds87704462021-08-24 14:42:39 -0500158 searchIndex = basePrivilegeCount;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 endIndex = privilegeNames.size();
160 }
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800161
Ed Tanous1abe55e2018-09-05 08:30:59 -0700162 for (; searchIndex < endIndex; searchIndex++)
163 {
164 if (privilegeBitset.test(searchIndex))
165 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000166 activePrivileges.emplace_back(privilegeNames[searchIndex]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700167 }
168 }
169
170 return activePrivileges;
171 }
172
173 /**
174 * @brief Determines if this Privilege set is a superset of the given
175 * privilege set
176 *
177 * @param[in] privilege Privilege to be checked
178 *
179 * @return None
180 *
181 */
182 bool isSupersetOf(const Privileges& p) const
183 {
184 return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset;
185 }
186
Joseph Reynolds3bf4e632020-02-06 14:44:32 -0600187 /**
188 * @brief Returns the intersection of two Privilege sets.
189 *
190 * @param[in] privilege Privilege set to intersect with.
191 *
192 * @return The new Privilege set.
193 *
194 */
195 Privileges intersection(const Privileges& p) const
196 {
197 return Privileges{privilegeBitset & p.privilegeBitset};
198 }
199
Ed Tanous1abe55e2018-09-05 08:30:59 -0700200 private:
Ed Tanous4e23a442022-06-06 09:57:26 -0700201 explicit Privileges(const std::bitset<maxPrivilegeCount>& p) :
202 privilegeBitset{p}
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500203 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700204 std::bitset<maxPrivilegeCount> privilegeBitset = 0;
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100205};
206
Ratan Gupta6f359562019-04-03 10:39:08 +0530207inline const Privileges& getUserPrivileges(const std::string& userRole)
208{
209 // Redfish privilege : Administrator
210 if (userRole == "priv-admin")
211 {
212 static Privileges admin{"Login", "ConfigureManager", "ConfigureSelf",
213 "ConfigureUsers", "ConfigureComponents"};
214 return admin;
215 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700216 if (userRole == "priv-operator")
Ratan Gupta6f359562019-04-03 10:39:08 +0530217 {
218 // Redfish privilege : Operator
219 static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"};
220 return op;
221 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700222 if (userRole == "priv-user")
Ratan Gupta6f359562019-04-03 10:39:08 +0530223 {
224 // Redfish privilege : Readonly
225 static Privileges readOnly{"Login", "ConfigureSelf"};
226 return readOnly;
227 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700228 // Redfish privilege : NoAccess
229 static Privileges noaccess;
230 return noaccess;
Ratan Gupta6f359562019-04-03 10:39:08 +0530231}
232
Joseph Reynolds900f9492019-11-25 15:37:29 -0600233/**
234 * @brief The OperationMap represents the privileges required for a
235 * single entity (URI). It maps from the allowable verbs to the
236 * privileges required to use that operation.
237 *
238 * This represents the Redfish "Privilege AND and OR syntax" as given
239 * in the spec and shown in the Privilege Registry. This does not
240 * implement any Redfish property overrides, subordinate overrides, or
241 * resource URI overrides. This does not implement the limitation of
242 * the ConfigureSelf privilege to operate only on your own account or
243 * session.
244 **/
Ed Tanouse0d918b2018-03-27 17:41:04 -0700245using OperationMap = boost::container::flat_map<boost::beast::http::verb,
246 std::vector<Privileges>>;
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100247
Joseph Reynolds900f9492019-11-25 15:37:29 -0600248/* @brief Checks if user is allowed to call an operation
249 *
250 * @param[in] operationPrivilegesRequired Privileges required
251 * @param[in] userPrivileges Privileges the user has
252 *
253 * @return True if operation is allowed, false otherwise
254 */
255inline bool isOperationAllowedWithPrivileges(
256 const std::vector<Privileges>& operationPrivilegesRequired,
257 const Privileges& userPrivileges)
258{
259 // If there are no privileges assigned, there are no privileges required
260 if (operationPrivilegesRequired.empty())
261 {
262 return true;
263 }
Ed Tanous9eb808c2022-01-25 10:19:23 -0800264 for (const auto& requiredPrivileges : operationPrivilegesRequired)
Joseph Reynolds900f9492019-11-25 15:37:29 -0600265 {
Andrew Geissler54fbf172021-11-11 15:59:30 -0600266 BMCWEB_LOG_DEBUG << "Checking operation privileges...";
Joseph Reynolds900f9492019-11-25 15:37:29 -0600267 if (userPrivileges.isSupersetOf(requiredPrivileges))
268 {
Andrew Geissler54fbf172021-11-11 15:59:30 -0600269 BMCWEB_LOG_DEBUG << "...success";
Joseph Reynolds900f9492019-11-25 15:37:29 -0600270 return true;
271 }
272 }
273 return false;
274}
275
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100276/**
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800277 * @brief Checks if given privileges allow to call an HTTP method
278 *
279 * @param[in] method HTTP method
280 * @param[in] user Privileges
281 *
282 * @return True if method allowed, false otherwise
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100283 *
284 */
Ed Tanouse0d918b2018-03-27 17:41:04 -0700285inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method,
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800286 const OperationMap& operationMap,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700287 const Privileges& userPrivileges)
288{
289 const auto& it = operationMap.find(method);
290 if (it == operationMap.end())
291 {
292 return false;
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800293 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294
Joseph Reynolds900f9492019-11-25 15:37:29 -0600295 return isOperationAllowedWithPrivileges(it->second, userPrivileges);
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800296}
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100297
Ed Tanous1abe55e2018-09-05 08:30:59 -0700298} // namespace redfish