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