xref: /openbmc/bmcweb/redfish-core/include/privileges.hpp (revision aecb47a427e201f685369cb2eb94dc524358ea19)
1 /*
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 
18 #include <bitset>
19 #include <cstdint>
20 #include "crow.h"
21 #include <boost/container/flat_map.hpp>
22 #include <boost/optional.hpp>
23 
24 namespace redfish {
25 
26 class PrivilegeProvider;
27 
28 enum class PrivilegeType { BASE, OEM };
29 
30 /** @brief Max number of privileges per type  */
31 constexpr const size_t MAX_PRIVILEGE_COUNT = 32;
32 using privilegeBitset = std::bitset<MAX_PRIVILEGE_COUNT>;
33 
34 /**
35  * @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.
55  */
56 class 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 
120  private:
121   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;
145 };
146 
147 /**
148  * @brief  Class used to store privileges for Redfish entities
149  */
150 class EntityPrivileges {
151  public:
152   /**
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);
185   }
186 
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;
193 };
194 
195 /**
196  * @brief  Class used to:
197  *         -  read the privilege_registry.json file
198  *         -  provide EntityPrivileges objects to callers
199  *
200  *         To save runtime memory, object of this class should
201  *         exist only for the time required to install all Nodes
202  */
203 class PrivilegeProvider {
204  public:
205   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     }
217   }
218 
219   /**
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;
240 };
241 
242 }  // namespace redfish
243 
244