xref: /openbmc/bmcweb/features/redfish/include/privileges.hpp (revision aecb47a427e201f685369cb2eb94dc524358ea19)
186e1b661SBorawski.Lukasz /*
286e1b661SBorawski.Lukasz // Copyright (c) 2018 Intel Corporation
386e1b661SBorawski.Lukasz //
486e1b661SBorawski.Lukasz // Licensed under the Apache License, Version 2.0 (the "License");
586e1b661SBorawski.Lukasz // you may not use this file except in compliance with the License.
686e1b661SBorawski.Lukasz // You may obtain a copy of the License at
786e1b661SBorawski.Lukasz //
886e1b661SBorawski.Lukasz //      http://www.apache.org/licenses/LICENSE-2.0
986e1b661SBorawski.Lukasz //
1086e1b661SBorawski.Lukasz // Unless required by applicable law or agreed to in writing, software
1186e1b661SBorawski.Lukasz // distributed under the License is distributed on an "AS IS" BASIS,
1286e1b661SBorawski.Lukasz // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1386e1b661SBorawski.Lukasz // See the License for the specific language governing permissions and
1486e1b661SBorawski.Lukasz // limitations under the License.
1586e1b661SBorawski.Lukasz */
1686e1b661SBorawski.Lukasz #pragma once
1786e1b661SBorawski.Lukasz 
18*aecb47a4SBorawski.Lukasz #include <bitset>
19*aecb47a4SBorawski.Lukasz #include <cstdint>
20*aecb47a4SBorawski.Lukasz #include "crow.h"
21*aecb47a4SBorawski.Lukasz #include <boost/container/flat_map.hpp>
22*aecb47a4SBorawski.Lukasz #include <boost/optional.hpp>
23*aecb47a4SBorawski.Lukasz 
2486e1b661SBorawski.Lukasz namespace redfish {
2586e1b661SBorawski.Lukasz 
26*aecb47a4SBorawski.Lukasz class PrivilegeProvider;
27*aecb47a4SBorawski.Lukasz 
28*aecb47a4SBorawski.Lukasz enum class PrivilegeType { BASE, OEM };
29*aecb47a4SBorawski.Lukasz 
30*aecb47a4SBorawski.Lukasz /** @brief Max number of privileges per type  */
31*aecb47a4SBorawski.Lukasz constexpr const size_t MAX_PRIVILEGE_COUNT = 32;
32*aecb47a4SBorawski.Lukasz using privilegeBitset = std::bitset<MAX_PRIVILEGE_COUNT>;
33*aecb47a4SBorawski.Lukasz 
3486e1b661SBorawski.Lukasz /**
35*aecb47a4SBorawski.Lukasz  * @brief Redfish privileges
36*aecb47a4SBorawski.Lukasz  *
37*aecb47a4SBorawski.Lukasz  *        Entity privileges and user privileges are represented by this class.
38*aecb47a4SBorawski.Lukasz  *
39*aecb47a4SBorawski.Lukasz  *        Each incoming connection requires a comparison between privileges held
40*aecb47a4SBorawski.Lukasz  *        by the user issuing a request and the target entity's privileges.
41*aecb47a4SBorawski.Lukasz  *
42*aecb47a4SBorawski.Lukasz  *        To ensure best runtime performance of this comparison, privileges
43*aecb47a4SBorawski.Lukasz  *        are represented as bitsets. Each bit in the bitset corresponds to a
44*aecb47a4SBorawski.Lukasz  *        unique privilege name.
45*aecb47a4SBorawski.Lukasz  *
46*aecb47a4SBorawski.Lukasz  *        Privilege names are read from the privilege_registry.json file and
47*aecb47a4SBorawski.Lukasz  *        stored in flat maps.
48*aecb47a4SBorawski.Lukasz  *
49*aecb47a4SBorawski.Lukasz  *        A bit is set if the privilege is required (entity domain) or granted
50*aecb47a4SBorawski.Lukasz  *        (user domain) and false otherwise.
51*aecb47a4SBorawski.Lukasz  *
52*aecb47a4SBorawski.Lukasz  *        Bitset index to privilege name mapping depends on the order in which
53*aecb47a4SBorawski.Lukasz  *        privileges are defined in PrivilegesUsed and OEMPrivilegesUsed arrays
54*aecb47a4SBorawski.Lukasz  *        in the privilege_registry.json.
5586e1b661SBorawski.Lukasz  */
56*aecb47a4SBorawski.Lukasz class Privileges {
57*aecb47a4SBorawski.Lukasz  public:
58*aecb47a4SBorawski.Lukasz   /**
59*aecb47a4SBorawski.Lukasz    * @brief Retrieves the base privileges bitset
60*aecb47a4SBorawski.Lukasz    *
61*aecb47a4SBorawski.Lukasz    * @return          Bitset representation of base Redfish privileges
62*aecb47a4SBorawski.Lukasz    */
63*aecb47a4SBorawski.Lukasz   privilegeBitset getBasePrivilegeBitset() const { return basePrivilegeBitset; }
64*aecb47a4SBorawski.Lukasz 
65*aecb47a4SBorawski.Lukasz   /**
66*aecb47a4SBorawski.Lukasz    * @brief Retrieves the OEM privileges bitset
67*aecb47a4SBorawski.Lukasz    *
68*aecb47a4SBorawski.Lukasz    * @return          Bitset representation of OEM Redfish privileges
69*aecb47a4SBorawski.Lukasz    */
70*aecb47a4SBorawski.Lukasz   privilegeBitset getOEMPrivilegeBitset() const { return oemPrivilegeBitset; }
71*aecb47a4SBorawski.Lukasz 
72*aecb47a4SBorawski.Lukasz   /**
73*aecb47a4SBorawski.Lukasz    * @brief Sets given privilege in the bitset
74*aecb47a4SBorawski.Lukasz    *
75*aecb47a4SBorawski.Lukasz    * @param[in] privilege  Privilege to be set
76*aecb47a4SBorawski.Lukasz    *
77*aecb47a4SBorawski.Lukasz    * @return               None
78*aecb47a4SBorawski.Lukasz    */
79*aecb47a4SBorawski.Lukasz   void setSinglePrivilege(const std::string& privilege) {
80*aecb47a4SBorawski.Lukasz     auto index = getBitsetIndexForPrivilege(privilege, PrivilegeType::BASE);
81*aecb47a4SBorawski.Lukasz     if (index) {
82*aecb47a4SBorawski.Lukasz       basePrivilegeBitset.set(*index);
83*aecb47a4SBorawski.Lukasz       return;
84*aecb47a4SBorawski.Lukasz     }
85*aecb47a4SBorawski.Lukasz 
86*aecb47a4SBorawski.Lukasz     index = getBitsetIndexForPrivilege(privilege, PrivilegeType::OEM);
87*aecb47a4SBorawski.Lukasz     if (index) {
88*aecb47a4SBorawski.Lukasz       oemPrivilegeBitset.set(*index);
89*aecb47a4SBorawski.Lukasz     }
90*aecb47a4SBorawski.Lukasz   }
91*aecb47a4SBorawski.Lukasz 
92*aecb47a4SBorawski.Lukasz   /**
93*aecb47a4SBorawski.Lukasz    * @brief Retrieves names of all active privileges for a given type
94*aecb47a4SBorawski.Lukasz    *
95*aecb47a4SBorawski.Lukasz    * @param[in] type    Base or OEM
96*aecb47a4SBorawski.Lukasz    *
97*aecb47a4SBorawski.Lukasz    * @return            Vector of active privileges
98*aecb47a4SBorawski.Lukasz    */
99*aecb47a4SBorawski.Lukasz   std::vector<std::string> getActivePrivilegeNames(
100*aecb47a4SBorawski.Lukasz       const PrivilegeType type) const {
101*aecb47a4SBorawski.Lukasz     std::vector<std::string> activePrivileges;
102*aecb47a4SBorawski.Lukasz 
103*aecb47a4SBorawski.Lukasz     if (type == PrivilegeType::BASE) {
104*aecb47a4SBorawski.Lukasz       for (const auto& pair : basePrivNameToIndexMap) {
105*aecb47a4SBorawski.Lukasz         if (basePrivilegeBitset.test(pair.second)) {
106*aecb47a4SBorawski.Lukasz           activePrivileges.emplace_back(pair.first);
107*aecb47a4SBorawski.Lukasz         }
108*aecb47a4SBorawski.Lukasz       }
109*aecb47a4SBorawski.Lukasz     } else {
110*aecb47a4SBorawski.Lukasz       for (const auto& pair : oemPrivNameToIndexMap) {
111*aecb47a4SBorawski.Lukasz         if (oemPrivilegeBitset.test(pair.second)) {
112*aecb47a4SBorawski.Lukasz           activePrivileges.emplace_back(pair.first);
113*aecb47a4SBorawski.Lukasz         }
114*aecb47a4SBorawski.Lukasz       }
115*aecb47a4SBorawski.Lukasz     }
116*aecb47a4SBorawski.Lukasz 
117*aecb47a4SBorawski.Lukasz     return activePrivileges;
118*aecb47a4SBorawski.Lukasz   }
119*aecb47a4SBorawski.Lukasz 
12086e1b661SBorawski.Lukasz  private:
121*aecb47a4SBorawski.Lukasz   boost::optional<size_t> getBitsetIndexForPrivilege(
122*aecb47a4SBorawski.Lukasz       const std::string& privilege, const PrivilegeType type) const {
123*aecb47a4SBorawski.Lukasz     if (type == PrivilegeType::BASE) {
124*aecb47a4SBorawski.Lukasz       const auto pair = basePrivNameToIndexMap.find(privilege);
125*aecb47a4SBorawski.Lukasz       if (pair != basePrivNameToIndexMap.end()) {
126*aecb47a4SBorawski.Lukasz         return pair->second;
127*aecb47a4SBorawski.Lukasz       }
128*aecb47a4SBorawski.Lukasz     } else {
129*aecb47a4SBorawski.Lukasz       const auto pair = oemPrivNameToIndexMap.find(privilege);
130*aecb47a4SBorawski.Lukasz       if (pair != oemPrivNameToIndexMap.end()) {
131*aecb47a4SBorawski.Lukasz         return pair->second;
132*aecb47a4SBorawski.Lukasz       }
133*aecb47a4SBorawski.Lukasz     }
134*aecb47a4SBorawski.Lukasz 
135*aecb47a4SBorawski.Lukasz     return boost::none;
136*aecb47a4SBorawski.Lukasz   }
137*aecb47a4SBorawski.Lukasz 
138*aecb47a4SBorawski.Lukasz   privilegeBitset basePrivilegeBitset;
139*aecb47a4SBorawski.Lukasz   privilegeBitset oemPrivilegeBitset;
140*aecb47a4SBorawski.Lukasz 
141*aecb47a4SBorawski.Lukasz   static boost::container::flat_map<std::string, size_t> basePrivNameToIndexMap;
142*aecb47a4SBorawski.Lukasz   static boost::container::flat_map<std::string, size_t> oemPrivNameToIndexMap;
143*aecb47a4SBorawski.Lukasz 
144*aecb47a4SBorawski.Lukasz   friend class PrivilegeProvider;
14586e1b661SBorawski.Lukasz };
14686e1b661SBorawski.Lukasz 
14786e1b661SBorawski.Lukasz /**
148*aecb47a4SBorawski.Lukasz  * @brief  Class used to store privileges for Redfish entities
14986e1b661SBorawski.Lukasz  */
15086e1b661SBorawski.Lukasz class EntityPrivileges {
15186e1b661SBorawski.Lukasz  public:
152*aecb47a4SBorawski.Lukasz   /**
153*aecb47a4SBorawski.Lukasz    * @brief Checks if a user is allowed to call an HTTP method
154*aecb47a4SBorawski.Lukasz    *
155*aecb47a4SBorawski.Lukasz    * @param[in] method       HTTP method
156*aecb47a4SBorawski.Lukasz    * @param[in] user         Username
157*aecb47a4SBorawski.Lukasz    *
158*aecb47a4SBorawski.Lukasz    * @return                 True if method allowed, false otherwise
159*aecb47a4SBorawski.Lukasz    */
160*aecb47a4SBorawski.Lukasz   bool isMethodAllowedForUser(const crow::HTTPMethod method,
161*aecb47a4SBorawski.Lukasz                               const std::string& user) const;
162*aecb47a4SBorawski.Lukasz 
163*aecb47a4SBorawski.Lukasz   /**
164*aecb47a4SBorawski.Lukasz    * @brief Checks if given privileges allow to call an HTTP method
165*aecb47a4SBorawski.Lukasz    *
166*aecb47a4SBorawski.Lukasz    * @param[in] method       HTTP method
167*aecb47a4SBorawski.Lukasz    * @param[in] user         Privileges
168*aecb47a4SBorawski.Lukasz    *
169*aecb47a4SBorawski.Lukasz    * @return                 True if method allowed, false otherwise
170*aecb47a4SBorawski.Lukasz    */
171*aecb47a4SBorawski.Lukasz   bool isMethodAllowedWithPrivileges(const crow::HTTPMethod method,
172*aecb47a4SBorawski.Lukasz                                      const Privileges& userPrivileges) const;
173*aecb47a4SBorawski.Lukasz 
174*aecb47a4SBorawski.Lukasz   /**
175*aecb47a4SBorawski.Lukasz    * @brief Sets required privileges for a method on a given entity
176*aecb47a4SBorawski.Lukasz    *
177*aecb47a4SBorawski.Lukasz    * @param[in] method       HTTP method
178*aecb47a4SBorawski.Lukasz    * @param[in] privileges   Required privileges
179*aecb47a4SBorawski.Lukasz    *
180*aecb47a4SBorawski.Lukasz    * @return                 None
181*aecb47a4SBorawski.Lukasz    */
182*aecb47a4SBorawski.Lukasz   void addPrivilegesRequiredByMethod(const crow::HTTPMethod method,
183*aecb47a4SBorawski.Lukasz                                      const Privileges& privileges) {
184*aecb47a4SBorawski.Lukasz     methodToPrivilegeMap[method].push_back(privileges);
18586e1b661SBorawski.Lukasz   }
186*aecb47a4SBorawski.Lukasz 
187*aecb47a4SBorawski.Lukasz  private:
188*aecb47a4SBorawski.Lukasz   bool verifyPrivileges(const privilegeBitset userPrivilegeBitset,
189*aecb47a4SBorawski.Lukasz                         const privilegeBitset requiredPrivilegeBitset) const;
190*aecb47a4SBorawski.Lukasz 
191*aecb47a4SBorawski.Lukasz   boost::container::flat_map<crow::HTTPMethod, std::vector<Privileges>>
192*aecb47a4SBorawski.Lukasz       methodToPrivilegeMap;
19386e1b661SBorawski.Lukasz };
19486e1b661SBorawski.Lukasz 
19586e1b661SBorawski.Lukasz /**
19686e1b661SBorawski.Lukasz  * @brief  Class used to:
197*aecb47a4SBorawski.Lukasz  *         -  read the privilege_registry.json file
198*aecb47a4SBorawski.Lukasz  *         -  provide EntityPrivileges objects to callers
19986e1b661SBorawski.Lukasz  *
200*aecb47a4SBorawski.Lukasz  *         To save runtime memory, object of this class should
201*aecb47a4SBorawski.Lukasz  *         exist only for the time required to install all Nodes
20286e1b661SBorawski.Lukasz  */
20386e1b661SBorawski.Lukasz class PrivilegeProvider {
20486e1b661SBorawski.Lukasz  public:
205*aecb47a4SBorawski.Lukasz   PrivilegeProvider(const std::string& privilegeRegistryPath) {
206*aecb47a4SBorawski.Lukasz     // TODO: read this path from the configuration once its available
207*aecb47a4SBorawski.Lukasz     std::ifstream privilegeRegistryFile{privilegeRegistryPath};
208*aecb47a4SBorawski.Lukasz 
209*aecb47a4SBorawski.Lukasz     if (privilegeRegistryFile.is_open()) {
210*aecb47a4SBorawski.Lukasz       if (!loadPrivilegesFromFile(privilegeRegistryFile)) {
211*aecb47a4SBorawski.Lukasz         privilegeRegistryJson.clear();
212*aecb47a4SBorawski.Lukasz         CROW_LOG_ERROR << "Couldn't parse privilege_registry.json";
213*aecb47a4SBorawski.Lukasz       }
214*aecb47a4SBorawski.Lukasz     } else {
215*aecb47a4SBorawski.Lukasz       CROW_LOG_ERROR << "Couldn't open privilege_registry.json";
216*aecb47a4SBorawski.Lukasz     }
21786e1b661SBorawski.Lukasz   }
21886e1b661SBorawski.Lukasz 
219*aecb47a4SBorawski.Lukasz   /**
220*aecb47a4SBorawski.Lukasz    * @brief Gets required privileges for a certain entity type
221*aecb47a4SBorawski.Lukasz    *
222*aecb47a4SBorawski.Lukasz    * @param[in] entityUrl    Entity url
223*aecb47a4SBorawski.Lukasz    * @param[in] entityType   Entity type
224*aecb47a4SBorawski.Lukasz    *
225*aecb47a4SBorawski.Lukasz    * @return                 EntityPrivilege object
226*aecb47a4SBorawski.Lukasz    */
227*aecb47a4SBorawski.Lukasz   EntityPrivileges getPrivilegesRequiredByEntity(
228*aecb47a4SBorawski.Lukasz       const std::string& entityUrl, const std::string& entityType) const;
229*aecb47a4SBorawski.Lukasz 
230*aecb47a4SBorawski.Lukasz  private:
231*aecb47a4SBorawski.Lukasz   bool loadPrivilegesFromFile(std::ifstream& privilegeRegistryFile);
232*aecb47a4SBorawski.Lukasz   bool privilegeRegistryHasRequiredFields() const;
233*aecb47a4SBorawski.Lukasz   bool parseOperationMap(const nlohmann::json& operationMap,
234*aecb47a4SBorawski.Lukasz                          EntityPrivileges& entityPrivileges) const;
235*aecb47a4SBorawski.Lukasz   bool fillPrivilegeMap(const nlohmann::json& privilegesUsed,
236*aecb47a4SBorawski.Lukasz                         boost::container::flat_map<std::string, size_t>&
237*aecb47a4SBorawski.Lukasz                             privilegeToIndexMap) const;
238*aecb47a4SBorawski.Lukasz 
239*aecb47a4SBorawski.Lukasz   nlohmann::json privilegeRegistryJson;
24086e1b661SBorawski.Lukasz };
24186e1b661SBorawski.Lukasz 
24286e1b661SBorawski.Lukasz }  // namespace redfish
24386e1b661SBorawski.Lukasz 
244