xref: /openbmc/bmcweb/features/redfish/include/privileges.hpp (revision 3ebd75f75e769532bb5bfa53d1acc81655a26ef0)
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 
18aecb47a4SBorawski.Lukasz #include <bitset>
19aecb47a4SBorawski.Lukasz #include <cstdint>
20aecb47a4SBorawski.Lukasz #include "crow.h"
21aecb47a4SBorawski.Lukasz #include <boost/container/flat_map.hpp>
22aecb47a4SBorawski.Lukasz #include <boost/optional.hpp>
23aecb47a4SBorawski.Lukasz 
2486e1b661SBorawski.Lukasz namespace redfish {
2586e1b661SBorawski.Lukasz 
26aecb47a4SBorawski.Lukasz enum class PrivilegeType { BASE, OEM };
27aecb47a4SBorawski.Lukasz 
28aecb47a4SBorawski.Lukasz /** @brief Max number of privileges per type  */
29aecb47a4SBorawski.Lukasz constexpr const size_t MAX_PRIVILEGE_COUNT = 32;
3043a095abSBorawski.Lukasz 
31aecb47a4SBorawski.Lukasz using privilegeBitset = std::bitset<MAX_PRIVILEGE_COUNT>;
32aecb47a4SBorawski.Lukasz 
3343a095abSBorawski.Lukasz /** @brief Number of mappings must be <= MAX_PRIVILEGE_COUNT */
34*3ebd75f7SEd Tanous static const std::vector<std::string> privilegeNames{
35*3ebd75f7SEd Tanous     "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
36*3ebd75f7SEd Tanous     "ConfigureUsers"};
3743a095abSBorawski.Lukasz 
3843a095abSBorawski.Lukasz /** @brief Number of mappings must be <= MAX_PRIVILEGE_COUNT */
39*3ebd75f7SEd Tanous static const std::vector<std::string> oemPrivilegeNames{};
4043a095abSBorawski.Lukasz 
4186e1b661SBorawski.Lukasz /**
42aecb47a4SBorawski.Lukasz  * @brief Redfish privileges
43aecb47a4SBorawski.Lukasz  *
44aecb47a4SBorawski.Lukasz  *        Entity privileges and user privileges are represented by this class.
45aecb47a4SBorawski.Lukasz  *
46aecb47a4SBorawski.Lukasz  *        Each incoming connection requires a comparison between privileges held
47aecb47a4SBorawski.Lukasz  *        by the user issuing a request and the target entity's privileges.
48aecb47a4SBorawski.Lukasz  *
49aecb47a4SBorawski.Lukasz  *        To ensure best runtime performance of this comparison, privileges
50aecb47a4SBorawski.Lukasz  *        are represented as bitsets. Each bit in the bitset corresponds to a
51aecb47a4SBorawski.Lukasz  *        unique privilege name.
52aecb47a4SBorawski.Lukasz  *
53aecb47a4SBorawski.Lukasz  *        A bit is set if the privilege is required (entity domain) or granted
54aecb47a4SBorawski.Lukasz  *        (user domain) and false otherwise.
55aecb47a4SBorawski.Lukasz  *
5686e1b661SBorawski.Lukasz  */
57aecb47a4SBorawski.Lukasz class Privileges {
58aecb47a4SBorawski.Lukasz  public:
59aecb47a4SBorawski.Lukasz   /**
6043a095abSBorawski.Lukasz    * @brief Constructs object without any privileges active
6143a095abSBorawski.Lukasz    *
6243a095abSBorawski.Lukasz    */
6343a095abSBorawski.Lukasz   Privileges() = default;
6443a095abSBorawski.Lukasz 
6543a095abSBorawski.Lukasz   /**
6643a095abSBorawski.Lukasz    * @brief Constructs object with given privileges active
6743a095abSBorawski.Lukasz    *
6843a095abSBorawski.Lukasz    * @param[in] privilegeList  List of privileges to be activated
6943a095abSBorawski.Lukasz    *
7043a095abSBorawski.Lukasz    */
71*3ebd75f7SEd Tanous   Privileges(std::initializer_list<const char*> privilegeList) {
72*3ebd75f7SEd Tanous     for (const char* privilege : privilegeList) {
73*3ebd75f7SEd Tanous       if (!setSinglePrivilege(privilege)) {
74*3ebd75f7SEd Tanous         CROW_LOG_CRITICAL << "Unable to set privilege " << privilege
75*3ebd75f7SEd Tanous                           << "in constructor";
7643a095abSBorawski.Lukasz       }
7743a095abSBorawski.Lukasz     }
78*3ebd75f7SEd Tanous   }
79aecb47a4SBorawski.Lukasz 
80aecb47a4SBorawski.Lukasz   /**
81aecb47a4SBorawski.Lukasz    * @brief Sets given privilege in the bitset
82aecb47a4SBorawski.Lukasz    *
83aecb47a4SBorawski.Lukasz    * @param[in] privilege  Privilege to be set
84aecb47a4SBorawski.Lukasz    *
85aecb47a4SBorawski.Lukasz    * @return               None
8643a095abSBorawski.Lukasz    *
87aecb47a4SBorawski.Lukasz    */
88*3ebd75f7SEd Tanous   bool setSinglePrivilege(const char* privilege) {
89*3ebd75f7SEd Tanous     int32_t index = getBitsetIndexForPrivilege(privilege, PrivilegeType::BASE);
90*3ebd75f7SEd Tanous     if (index >= 0) {
91*3ebd75f7SEd Tanous       basePrivilegeBitset.set(index);
92*3ebd75f7SEd Tanous       return true;
93aecb47a4SBorawski.Lukasz     }
94aecb47a4SBorawski.Lukasz 
95aecb47a4SBorawski.Lukasz     index = getBitsetIndexForPrivilege(privilege, PrivilegeType::OEM);
96*3ebd75f7SEd Tanous     if (index >= 0) {
97*3ebd75f7SEd Tanous       oemPrivilegeBitset.set(index);
98*3ebd75f7SEd Tanous       return true;
99aecb47a4SBorawski.Lukasz     }
100*3ebd75f7SEd Tanous     return false;
101*3ebd75f7SEd Tanous   }
102*3ebd75f7SEd Tanous 
103*3ebd75f7SEd Tanous   /**
104*3ebd75f7SEd Tanous    * @brief Sets given privilege in the bitset
105*3ebd75f7SEd Tanous    *
106*3ebd75f7SEd Tanous    * @param[in] privilege  Privilege to be set
107*3ebd75f7SEd Tanous    *
108*3ebd75f7SEd Tanous    * @return               None
109*3ebd75f7SEd Tanous    *
110*3ebd75f7SEd Tanous    */
111*3ebd75f7SEd Tanous   bool setSinglePrivilege(const std::string& privilege) {
112*3ebd75f7SEd Tanous     return setSinglePrivilege(privilege.c_str());
113aecb47a4SBorawski.Lukasz   }
114aecb47a4SBorawski.Lukasz 
115aecb47a4SBorawski.Lukasz   /**
116aecb47a4SBorawski.Lukasz    * @brief Retrieves names of all active privileges for a given type
117aecb47a4SBorawski.Lukasz    *
118aecb47a4SBorawski.Lukasz    * @param[in] type    Base or OEM
119aecb47a4SBorawski.Lukasz    *
120*3ebd75f7SEd Tanous    * @return            Vector of active privileges.  Pointers are valid until
121*3ebd75f7SEd Tanous    * the privilege structure is modified
12243a095abSBorawski.Lukasz    *
123aecb47a4SBorawski.Lukasz    */
124*3ebd75f7SEd Tanous   std::vector<const std::string*> getActivePrivilegeNames(
125aecb47a4SBorawski.Lukasz       const PrivilegeType type) const {
126*3ebd75f7SEd Tanous     std::vector<const std::string*> activePrivileges;
127aecb47a4SBorawski.Lukasz 
128aecb47a4SBorawski.Lukasz     if (type == PrivilegeType::BASE) {
129*3ebd75f7SEd Tanous       for (std::size_t index = 0; index < privilegeNames.size(); index++) {
130*3ebd75f7SEd Tanous         if (basePrivilegeBitset.test(index)) {
131*3ebd75f7SEd Tanous           activePrivileges.emplace_back(&privilegeNames[index]);
132aecb47a4SBorawski.Lukasz         }
133aecb47a4SBorawski.Lukasz       }
134aecb47a4SBorawski.Lukasz     } else {
135*3ebd75f7SEd Tanous       for (std::size_t index = 0; index < oemPrivilegeNames.size(); index++) {
136*3ebd75f7SEd Tanous         {
137*3ebd75f7SEd Tanous           if (oemPrivilegeBitset.test(index)) {
138*3ebd75f7SEd Tanous             activePrivileges.emplace_back(&oemPrivilegeNames[index]);
139aecb47a4SBorawski.Lukasz           }
140aecb47a4SBorawski.Lukasz         }
141aecb47a4SBorawski.Lukasz       }
142*3ebd75f7SEd Tanous     }
143aecb47a4SBorawski.Lukasz     return activePrivileges;
144aecb47a4SBorawski.Lukasz   }
145aecb47a4SBorawski.Lukasz 
146*3ebd75f7SEd Tanous   /**
147*3ebd75f7SEd Tanous    * @brief Determines if this Privilege set is a superset of the given
148*3ebd75f7SEd Tanous    * privilege set
149*3ebd75f7SEd Tanous    *
150*3ebd75f7SEd Tanous    * @param[in] privilege  Privilege to be checked
151*3ebd75f7SEd Tanous    *
152*3ebd75f7SEd Tanous    * @return               None
153*3ebd75f7SEd Tanous    *
154*3ebd75f7SEd Tanous    */
155*3ebd75f7SEd Tanous   bool isSupersetOf(const Privileges& p) const {
156*3ebd75f7SEd Tanous     bool has_base =
157*3ebd75f7SEd Tanous         (basePrivilegeBitset & p.basePrivilegeBitset) == p.basePrivilegeBitset;
158*3ebd75f7SEd Tanous 
159*3ebd75f7SEd Tanous     bool has_oem =
160*3ebd75f7SEd Tanous         (oemPrivilegeBitset & p.oemPrivilegeBitset) == p.oemPrivilegeBitset;
161*3ebd75f7SEd Tanous     return has_base & has_oem;
162*3ebd75f7SEd Tanous   }
163*3ebd75f7SEd Tanous 
16486e1b661SBorawski.Lukasz  private:
165*3ebd75f7SEd Tanous   int32_t getBitsetIndexForPrivilege(const char* privilege,
166*3ebd75f7SEd Tanous                                      const PrivilegeType type) const {
167aecb47a4SBorawski.Lukasz     if (type == PrivilegeType::BASE) {
168*3ebd75f7SEd Tanous       for (std::size_t index = 0; index < privilegeNames.size(); index++) {
169*3ebd75f7SEd Tanous         if (privilege == privilegeNames[index]) {
170*3ebd75f7SEd Tanous           return index;
171*3ebd75f7SEd Tanous         }
172aecb47a4SBorawski.Lukasz       }
173aecb47a4SBorawski.Lukasz     } else {
174*3ebd75f7SEd Tanous       for (std::size_t index = 0; index < oemPrivilegeNames.size(); index++) {
175*3ebd75f7SEd Tanous         if (privilege == oemPrivilegeNames[index]) {
176*3ebd75f7SEd Tanous           return index;
177*3ebd75f7SEd Tanous         }
178aecb47a4SBorawski.Lukasz       }
179aecb47a4SBorawski.Lukasz     }
180aecb47a4SBorawski.Lukasz 
181*3ebd75f7SEd Tanous     return -1;
182aecb47a4SBorawski.Lukasz   }
183aecb47a4SBorawski.Lukasz 
184*3ebd75f7SEd Tanous   privilegeBitset basePrivilegeBitset = 0;
185*3ebd75f7SEd Tanous   privilegeBitset oemPrivilegeBitset = 0;
18686e1b661SBorawski.Lukasz };
18786e1b661SBorawski.Lukasz 
18843a095abSBorawski.Lukasz using OperationMap =
18943a095abSBorawski.Lukasz     boost::container::flat_map<crow::HTTPMethod, std::vector<Privileges>>;
19043a095abSBorawski.Lukasz 
19143a095abSBorawski.Lukasz /**
192aecb47a4SBorawski.Lukasz  * @brief Checks if given privileges allow to call an HTTP method
193aecb47a4SBorawski.Lukasz  *
194aecb47a4SBorawski.Lukasz  * @param[in] method       HTTP method
195aecb47a4SBorawski.Lukasz  * @param[in] user         Privileges
196aecb47a4SBorawski.Lukasz  *
197aecb47a4SBorawski.Lukasz  * @return                 True if method allowed, false otherwise
19843a095abSBorawski.Lukasz  *
199aecb47a4SBorawski.Lukasz  */
200*3ebd75f7SEd Tanous inline bool isMethodAllowedWithPrivileges(const crow::HTTPMethod method,
201*3ebd75f7SEd Tanous                                           const OperationMap& operationMap,
202*3ebd75f7SEd Tanous                                           const Privileges& userPrivileges) {
203*3ebd75f7SEd Tanous   const auto& it = operationMap.find(method);
204*3ebd75f7SEd Tanous   if (it == operationMap.end()) {
20543a095abSBorawski.Lukasz     return false;
20643a095abSBorawski.Lukasz   }
207aecb47a4SBorawski.Lukasz 
208*3ebd75f7SEd Tanous   // If there are no privileges assigned, assume no privileges required
209*3ebd75f7SEd Tanous   if (it->second.empty()) {
21043a095abSBorawski.Lukasz     return true;
21143a095abSBorawski.Lukasz   }
212*3ebd75f7SEd Tanous 
213*3ebd75f7SEd Tanous   for (auto& requiredPrivileges : it->second) {
214*3ebd75f7SEd Tanous     if (userPrivileges.isSupersetOf(requiredPrivileges)) {
215*3ebd75f7SEd Tanous       return true;
216*3ebd75f7SEd Tanous     }
217*3ebd75f7SEd Tanous   }
21843a095abSBorawski.Lukasz   return false;
21986e1b661SBorawski.Lukasz }
220aecb47a4SBorawski.Lukasz 
221*3ebd75f7SEd Tanous /**
222*3ebd75f7SEd Tanous  * @brief Checks if a user is allowed to call an HTTP method
223*3ebd75f7SEd Tanous  *
224*3ebd75f7SEd Tanous  * @param[in] method       HTTP method
225*3ebd75f7SEd Tanous  * @param[in] user         Username
226*3ebd75f7SEd Tanous  *
227*3ebd75f7SEd Tanous  * @return                 True if method allowed, false otherwise
228*3ebd75f7SEd Tanous  *
229*3ebd75f7SEd Tanous  */
230*3ebd75f7SEd Tanous inline bool isMethodAllowedForUser(const crow::HTTPMethod method,
231*3ebd75f7SEd Tanous                                    const OperationMap& operationMap,
232*3ebd75f7SEd Tanous                                    const std::string& user) {
233*3ebd75f7SEd Tanous   // TODO: load user privileges from configuration as soon as its available
234*3ebd75f7SEd Tanous   // now we are granting all privileges to everyone.
235*3ebd75f7SEd Tanous   Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf",
236*3ebd75f7SEd Tanous                             "ConfigureUsers", "ConfigureComponents"};
237*3ebd75f7SEd Tanous 
238*3ebd75f7SEd Tanous   return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges);
23986e1b661SBorawski.Lukasz }
24086e1b661SBorawski.Lukasz 
24186e1b661SBorawski.Lukasz }  // namespace redfish
242