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