blob: 244110466b2527a24482634afb29ff728105479f [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
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010018#include <bitset>
19#include <cstdint>
20#include "crow.h"
21#include <boost/container/flat_map.hpp>
22#include <boost/optional.hpp>
23
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010024namespace redfish {
25
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010026class PrivilegeProvider;
27
28enum class PrivilegeType { BASE, OEM };
29
30/** @brief Max number of privileges per type */
31constexpr const size_t MAX_PRIVILEGE_COUNT = 32;
32using privilegeBitset = std::bitset<MAX_PRIVILEGE_COUNT>;
33
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010034/**
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010035 * @brief Redfish privileges
36 *
37 * Entity privileges and user privileges are represented by this class.
38 *
39 * Each incoming connection requires a comparison between privileges held
40 * by the user issuing a request and the target entity's privileges.
41 *
42 * To ensure best runtime performance of this comparison, privileges
43 * are represented as bitsets. Each bit in the bitset corresponds to a
44 * unique privilege name.
45 *
46 * Privilege names are read from the privilege_registry.json file and
47 * stored in flat maps.
48 *
49 * A bit is set if the privilege is required (entity domain) or granted
50 * (user domain) and false otherwise.
51 *
52 * Bitset index to privilege name mapping depends on the order in which
53 * privileges are defined in PrivilegesUsed and OEMPrivilegesUsed arrays
54 * in the privilege_registry.json.
Borawski.Lukasz86e1b662018-01-19 14:22:14 +010055 */
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010056class Privileges {
57 public:
58 /**
59 * @brief Retrieves the base privileges bitset
60 *
61 * @return Bitset representation of base Redfish privileges
62 */
63 privilegeBitset getBasePrivilegeBitset() const { return basePrivilegeBitset; }
64
65 /**
66 * @brief Retrieves the OEM privileges bitset
67 *
68 * @return Bitset representation of OEM Redfish privileges
69 */
70 privilegeBitset getOEMPrivilegeBitset() const { return oemPrivilegeBitset; }
71
72 /**
73 * @brief Sets given privilege in the bitset
74 *
75 * @param[in] privilege Privilege to be set
76 *
77 * @return None
78 */
79 void setSinglePrivilege(const std::string& privilege) {
80 auto index = getBitsetIndexForPrivilege(privilege, PrivilegeType::BASE);
81 if (index) {
82 basePrivilegeBitset.set(*index);
83 return;
84 }
85
86 index = getBitsetIndexForPrivilege(privilege, PrivilegeType::OEM);
87 if (index) {
88 oemPrivilegeBitset.set(*index);
89 }
90 }
91
92 /**
93 * @brief Retrieves names of all active privileges for a given type
94 *
95 * @param[in] type Base or OEM
96 *
97 * @return Vector of active privileges
98 */
99 std::vector<std::string> getActivePrivilegeNames(
100 const PrivilegeType type) const {
101 std::vector<std::string> activePrivileges;
102
103 if (type == PrivilegeType::BASE) {
104 for (const auto& pair : basePrivNameToIndexMap) {
105 if (basePrivilegeBitset.test(pair.second)) {
106 activePrivileges.emplace_back(pair.first);
107 }
108 }
109 } else {
110 for (const auto& pair : oemPrivNameToIndexMap) {
111 if (oemPrivilegeBitset.test(pair.second)) {
112 activePrivileges.emplace_back(pair.first);
113 }
114 }
115 }
116
117 return activePrivileges;
118 }
119
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100120 private:
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100121 boost::optional<size_t> getBitsetIndexForPrivilege(
122 const std::string& privilege, const PrivilegeType type) const {
123 if (type == PrivilegeType::BASE) {
124 const auto pair = basePrivNameToIndexMap.find(privilege);
125 if (pair != basePrivNameToIndexMap.end()) {
126 return pair->second;
127 }
128 } else {
129 const auto pair = oemPrivNameToIndexMap.find(privilege);
130 if (pair != oemPrivNameToIndexMap.end()) {
131 return pair->second;
132 }
133 }
134
135 return boost::none;
136 }
137
138 privilegeBitset basePrivilegeBitset;
139 privilegeBitset oemPrivilegeBitset;
140
141 static boost::container::flat_map<std::string, size_t> basePrivNameToIndexMap;
142 static boost::container::flat_map<std::string, size_t> oemPrivNameToIndexMap;
143
144 friend class PrivilegeProvider;
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100145};
146
147/**
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100148 * @brief Class used to store privileges for Redfish entities
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100149 */
150class EntityPrivileges {
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100151 public:
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100152 /**
153 * @brief Checks if a user is allowed to call an HTTP method
154 *
155 * @param[in] method HTTP method
156 * @param[in] user Username
157 *
158 * @return True if method allowed, false otherwise
159 */
160 bool isMethodAllowedForUser(const crow::HTTPMethod method,
161 const std::string& user) const;
162
163 /**
164 * @brief Checks if given privileges allow to call an HTTP method
165 *
166 * @param[in] method HTTP method
167 * @param[in] user Privileges
168 *
169 * @return True if method allowed, false otherwise
170 */
171 bool isMethodAllowedWithPrivileges(const crow::HTTPMethod method,
172 const Privileges& userPrivileges) const;
173
174 /**
175 * @brief Sets required privileges for a method on a given entity
176 *
177 * @param[in] method HTTP method
178 * @param[in] privileges Required privileges
179 *
180 * @return None
181 */
182 void addPrivilegesRequiredByMethod(const crow::HTTPMethod method,
183 const Privileges& privileges) {
184 methodToPrivilegeMap[method].push_back(privileges);
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100185 }
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100186
187 private:
188 bool verifyPrivileges(const privilegeBitset userPrivilegeBitset,
189 const privilegeBitset requiredPrivilegeBitset) const;
190
191 boost::container::flat_map<crow::HTTPMethod, std::vector<Privileges>>
192 methodToPrivilegeMap;
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100193};
194
195/**
196 * @brief Class used to:
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100197 * - read the privilege_registry.json file
198 * - provide EntityPrivileges objects to callers
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100199 *
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100200 * To save runtime memory, object of this class should
201 * exist only for the time required to install all Nodes
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100202 */
203class PrivilegeProvider {
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100204 public:
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100205 PrivilegeProvider(const std::string& privilegeRegistryPath) {
206 // TODO: read this path from the configuration once its available
207 std::ifstream privilegeRegistryFile{privilegeRegistryPath};
208
209 if (privilegeRegistryFile.is_open()) {
210 if (!loadPrivilegesFromFile(privilegeRegistryFile)) {
211 privilegeRegistryJson.clear();
212 CROW_LOG_ERROR << "Couldn't parse privilege_registry.json";
213 }
214 } else {
215 CROW_LOG_ERROR << "Couldn't open privilege_registry.json";
216 }
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100217 }
218
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +0100219 /**
220 * @brief Gets required privileges for a certain entity type
221 *
222 * @param[in] entityUrl Entity url
223 * @param[in] entityType Entity type
224 *
225 * @return EntityPrivilege object
226 */
227 EntityPrivileges getPrivilegesRequiredByEntity(
228 const std::string& entityUrl, const std::string& entityType) const;
229
230 private:
231 bool loadPrivilegesFromFile(std::ifstream& privilegeRegistryFile);
232 bool privilegeRegistryHasRequiredFields() const;
233 bool parseOperationMap(const nlohmann::json& operationMap,
234 EntityPrivileges& entityPrivileges) const;
235 bool fillPrivilegeMap(const nlohmann::json& privilegesUsed,
236 boost::container::flat_map<std::string, size_t>&
237 privilegeToIndexMap) const;
238
239 nlohmann::json privilegeRegistryJson;
Borawski.Lukasz86e1b662018-01-19 14:22:14 +0100240};
241
242} // namespace redfish
243