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