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 <logging.h>
19 
20 #include <boost/beast/http/verb.hpp>
21 #include <boost/container/flat_map.hpp>
22 
23 #include <array>
24 #include <bitset>
25 #include <cstdint>
26 #include <vector>
27 
28 namespace redfish
29 {
30 
31 enum class PrivilegeType
32 {
33     BASE,
34     OEM
35 };
36 
37 /** @brief A fixed array of compile time privileges  */
38 constexpr std::array<const char*, 5> basePrivileges{
39     "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
40     "ConfigureUsers"};
41 
42 constexpr const size_t basePrivilegeCount = basePrivileges.size();
43 
44 /** @brief Max number of privileges per type  */
45 constexpr const size_t maxPrivilegeCount = 32;
46 
47 /** @brief A vector of all privilege names and their indexes */
48 static const std::array<std::string, maxPrivilegeCount> privilegeNames{
49     "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
50     "ConfigureUsers"};
51 
52 /**
53  * @brief Redfish privileges
54  *
55  *        This implements a set of Redfish privileges.  These directly represent
56  *        user privileges and help represent entity privileges.
57  *
58  *        Each incoming Connection requires a comparison between privileges held
59  *        by the user issuing a request and the target entity's privileges.
60  *
61  *        To ensure best runtime performance of this comparison, privileges
62  *        are represented as bitsets. Each bit in the bitset corresponds to a
63  *        unique privilege name.
64  *
65  *        A bit is set if the privilege is required (entity domain) or granted
66  *        (user domain) and false otherwise.
67  *
68  */
69 class Privileges
70 {
71   public:
72     /**
73      * @brief Constructs object without any privileges active
74      *
75      */
76     Privileges() = default;
77 
78     /**
79      * @brief Constructs object with given privileges active
80      *
81      * @param[in] privilegeList  List of privileges to be activated
82      *
83      */
84     Privileges(std::initializer_list<const char*> privilegeList)
85     {
86         for (const char* privilege : privilegeList)
87         {
88             if (!setSinglePrivilege(privilege))
89             {
90                 BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege
91                                     << "in constructor";
92             }
93         }
94     }
95 
96     /**
97      * @brief Sets given privilege in the bitset
98      *
99      * @param[in] privilege  Privilege to be set
100      *
101      * @return               None
102      *
103      */
104     bool setSinglePrivilege(const std::string_view privilege)
105     {
106         for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
107              searchIndex++)
108         {
109             if (privilege == privilegeNames[searchIndex])
110             {
111                 privilegeBitset.set(searchIndex);
112                 return true;
113             }
114         }
115 
116         return false;
117     }
118 
119     /**
120      * @brief Resets the given privilege in the bitset
121      *
122      * @param[in] privilege  Privilege to be reset
123      *
124      * @return               None
125      *
126      */
127     bool resetSinglePrivilege(const char* privilege)
128     {
129         for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
130              searchIndex++)
131         {
132             if (privilege == privilegeNames[searchIndex])
133             {
134                 privilegeBitset.reset(searchIndex);
135                 return true;
136             }
137         }
138         return false;
139     }
140 
141     /**
142      * @brief Retrieves names of all active privileges for a given type
143      *
144      * @param[in] type    Base or OEM
145      *
146      * @return            Vector of active privileges.  Pointers are valid until
147      * the setSinglePrivilege is called, or the Privilege structure is destroyed
148      *
149      */
150     std::vector<std::string>
151         getActivePrivilegeNames(const PrivilegeType type) const
152     {
153         std::vector<std::string> activePrivileges;
154 
155         size_t searchIndex = 0;
156         size_t endIndex = basePrivilegeCount;
157         if (type == PrivilegeType::OEM)
158         {
159             searchIndex = basePrivilegeCount - 1;
160             endIndex = privilegeNames.size();
161         }
162 
163         for (; searchIndex < endIndex; searchIndex++)
164         {
165             if (privilegeBitset.test(searchIndex))
166             {
167                 activePrivileges.emplace_back(privilegeNames[searchIndex]);
168             }
169         }
170 
171         return activePrivileges;
172     }
173 
174     /**
175      * @brief Determines if this Privilege set is a superset of the given
176      * privilege set
177      *
178      * @param[in] privilege  Privilege to be checked
179      *
180      * @return               None
181      *
182      */
183     bool isSupersetOf(const Privileges& p) const
184     {
185         return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset;
186     }
187 
188     /**
189      * @brief Returns the intersection of two Privilege sets.
190      *
191      * @param[in] privilege  Privilege set to intersect with.
192      *
193      * @return               The new Privilege set.
194      *
195      */
196     Privileges intersection(const Privileges& p) const
197     {
198         return Privileges{privilegeBitset & p.privilegeBitset};
199     }
200 
201   private:
202     Privileges(const std::bitset<maxPrivilegeCount>& p) : privilegeBitset{p}
203     {}
204     std::bitset<maxPrivilegeCount> privilegeBitset = 0;
205 };
206 
207 inline const Privileges& getUserPrivileges(const std::string& userRole)
208 {
209     // Redfish privilege : Administrator
210     if (userRole == "priv-admin")
211     {
212         static Privileges admin{"Login", "ConfigureManager", "ConfigureSelf",
213                                 "ConfigureUsers", "ConfigureComponents"};
214         return admin;
215     }
216     else if (userRole == "priv-operator")
217     {
218         // Redfish privilege : Operator
219         static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"};
220         return op;
221     }
222     else if (userRole == "priv-user")
223     {
224         // Redfish privilege : Readonly
225         static Privileges readOnly{"Login", "ConfigureSelf"};
226         return readOnly;
227     }
228     else
229     {
230         // Redfish privilege : NoAccess
231         static Privileges noaccess;
232         return noaccess;
233     }
234 }
235 
236 /**
237  * @brief The OperationMap represents the privileges required for a
238  * single entity (URI).  It maps from the allowable verbs to the
239  * privileges required to use that operation.
240  *
241  * This represents the Redfish "Privilege AND and OR syntax" as given
242  * in the spec and shown in the Privilege Registry.  This does not
243  * implement any Redfish property overrides, subordinate overrides, or
244  * resource URI overrides.  This does not implement the limitation of
245  * the ConfigureSelf privilege to operate only on your own account or
246  * session.
247  **/
248 using OperationMap = boost::container::flat_map<boost::beast::http::verb,
249                                                 std::vector<Privileges>>;
250 
251 /* @brief Checks if user is allowed to call an operation
252  *
253  * @param[in] operationPrivilegesRequired   Privileges required
254  * @param[in] userPrivileges                Privileges the user has
255  *
256  * @return                 True if operation is allowed, false otherwise
257  */
258 inline bool isOperationAllowedWithPrivileges(
259     const std::vector<Privileges>& operationPrivilegesRequired,
260     const Privileges& userPrivileges)
261 {
262     // If there are no privileges assigned, there are no privileges required
263     if (operationPrivilegesRequired.empty())
264     {
265         return true;
266     }
267     for (auto& requiredPrivileges : operationPrivilegesRequired)
268     {
269         BMCWEB_LOG_ERROR << "Checking operation privileges...";
270         if (userPrivileges.isSupersetOf(requiredPrivileges))
271         {
272             BMCWEB_LOG_ERROR << "...success";
273             return true;
274         }
275     }
276     return false;
277 }
278 
279 /**
280  * @brief Checks if given privileges allow to call an HTTP method
281  *
282  * @param[in] method       HTTP method
283  * @param[in] user         Privileges
284  *
285  * @return                 True if method allowed, false otherwise
286  *
287  */
288 inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method,
289                                           const OperationMap& operationMap,
290                                           const Privileges& userPrivileges)
291 {
292     const auto& it = operationMap.find(method);
293     if (it == operationMap.end())
294     {
295         return false;
296     }
297 
298     return isOperationAllowedWithPrivileges(it->second, userPrivileges);
299 }
300 
301 /**
302  * @brief Checks if a user is allowed to call an HTTP method
303  *
304  * @param[in] method       HTTP method
305  * @param[in] user         Username
306  *
307  * @return                 True if method allowed, false otherwise
308  *
309  */
310 inline bool isMethodAllowedForUser(const boost::beast::http::verb method,
311                                    const OperationMap& operationMap,
312                                    const std::string&)
313 {
314     // TODO: load user privileges from configuration as soon as its available
315     // now we are granting all privileges to everyone.
316     Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf",
317                               "ConfigureUsers", "ConfigureComponents"};
318 
319     return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges);
320 }
321 
322 } // namespace redfish
323