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 <crow/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  *        Entity privileges and user privileges are represented by this class.
54  *
55  *        Each incoming Connection requires a comparison between privileges held
56  *        by the user issuing a request and the target entity's privileges.
57  *
58  *        To ensure best runtime performance of this comparison, privileges
59  *        are represented as bitsets. Each bit in the bitset corresponds to a
60  *        unique privilege name.
61  *
62  *        A bit is set if the privilege is required (entity domain) or granted
63  *        (user domain) and false otherwise.
64  *
65  */
66 class Privileges
67 {
68   public:
69     /**
70      * @brief Constructs object without any privileges active
71      *
72      */
73     Privileges() = default;
74 
75     /**
76      * @brief Constructs object with given privileges active
77      *
78      * @param[in] privilegeList  List of privileges to be activated
79      *
80      */
81     Privileges(std::initializer_list<const char*> privilegeList)
82     {
83         for (const char* privilege : privilegeList)
84         {
85             if (!setSinglePrivilege(privilege))
86             {
87                 BMCWEB_LOG_CRITICAL << "Unable to set privilege " << privilege
88                                     << "in constructor";
89             }
90         }
91     }
92 
93     /**
94      * @brief Sets given privilege in the bitset
95      *
96      * @param[in] privilege  Privilege to be set
97      *
98      * @return               None
99      *
100      */
101     bool setSinglePrivilege(const char* privilege)
102     {
103         for (size_t searchIndex = 0; searchIndex < privilegeNames.size();
104              searchIndex++)
105         {
106             if (privilege == privilegeNames[searchIndex])
107             {
108                 privilegeBitset.set(searchIndex);
109                 return true;
110             }
111         }
112 
113         return false;
114     }
115 
116     /**
117      * @brief Sets given privilege in the bitset
118      *
119      * @param[in] privilege  Privilege to be set
120      *
121      * @return               None
122      *
123      */
124     bool setSinglePrivilege(const std::string& privilege)
125     {
126         return setSinglePrivilege(privilege.c_str());
127     }
128 
129     /**
130      * @brief Retrieves names of all active privileges for a given type
131      *
132      * @param[in] type    Base or OEM
133      *
134      * @return            Vector of active privileges.  Pointers are valid until
135      * the setSinglePrivilege is called, or the Privilege structure is destroyed
136      *
137      */
138     std::vector<const std::string*>
139         getActivePrivilegeNames(const PrivilegeType type) const
140     {
141         std::vector<const std::string*> activePrivileges;
142 
143         size_t searchIndex = 0;
144         size_t endIndex = basePrivilegeCount;
145         if (type == PrivilegeType::OEM)
146         {
147             searchIndex = basePrivilegeCount - 1;
148             endIndex = privilegeNames.size();
149         }
150 
151         for (; searchIndex < endIndex; searchIndex++)
152         {
153             if (privilegeBitset.test(searchIndex))
154             {
155                 activePrivileges.emplace_back(&privilegeNames[searchIndex]);
156             }
157         }
158 
159         return activePrivileges;
160     }
161 
162     /**
163      * @brief Determines if this Privilege set is a superset of the given
164      * privilege set
165      *
166      * @param[in] privilege  Privilege to be checked
167      *
168      * @return               None
169      *
170      */
171     bool isSupersetOf(const Privileges& p) const
172     {
173         return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset;
174     }
175 
176   private:
177     std::bitset<maxPrivilegeCount> privilegeBitset = 0;
178 };
179 
180 inline const Privileges& getUserPrivileges(const std::string& userRole)
181 {
182     // Redfish privilege : Administrator
183     if (userRole == "priv-admin")
184     {
185         static Privileges admin{"Login", "ConfigureManager", "ConfigureSelf",
186                                 "ConfigureUsers", "ConfigureComponents"};
187         return admin;
188     }
189     else if (userRole == "priv-operator")
190     {
191         // Redfish privilege : Operator
192         static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"};
193         return op;
194     }
195     else
196     {
197         // Redfish privilege : Readonly
198         static Privileges readOnly{"Login", "ConfigureSelf"};
199         return readOnly;
200     }
201 }
202 
203 using OperationMap = boost::container::flat_map<boost::beast::http::verb,
204                                                 std::vector<Privileges>>;
205 
206 /**
207  * @brief Checks if given privileges allow to call an HTTP method
208  *
209  * @param[in] method       HTTP method
210  * @param[in] user         Privileges
211  *
212  * @return                 True if method allowed, false otherwise
213  *
214  */
215 inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method,
216                                           const OperationMap& operationMap,
217                                           const Privileges& userPrivileges)
218 {
219     const auto& it = operationMap.find(method);
220     if (it == operationMap.end())
221     {
222         return false;
223     }
224 
225     // If there are no privileges assigned, assume no privileges required
226     if (it->second.empty())
227     {
228         return true;
229     }
230 
231     for (auto& requiredPrivileges : it->second)
232     {
233         if (userPrivileges.isSupersetOf(requiredPrivileges))
234         {
235             return true;
236         }
237     }
238     return false;
239 }
240 
241 /**
242  * @brief Checks if a user is allowed to call an HTTP method
243  *
244  * @param[in] method       HTTP method
245  * @param[in] user         Username
246  *
247  * @return                 True if method allowed, false otherwise
248  *
249  */
250 inline bool isMethodAllowedForUser(const boost::beast::http::verb method,
251                                    const OperationMap& operationMap,
252                                    const std::string& user)
253 {
254     // TODO: load user privileges from configuration as soon as its available
255     // now we are granting all privileges to everyone.
256     Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf",
257                               "ConfigureUsers", "ConfigureComponents"};
258 
259     return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges);
260 }
261 
262 } // namespace redfish
263