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