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