xref: /openbmc/bmcweb/features/redfish/include/privileges.hpp (revision e0d918bc397350aa21af3dab9faa6e21748f6373)
186e1b661SBorawski.Lukasz /*
286e1b661SBorawski.Lukasz // Copyright (c) 2018 Intel Corporation
386e1b661SBorawski.Lukasz //
486e1b661SBorawski.Lukasz // Licensed under the Apache License, Version 2.0 (the "License");
586e1b661SBorawski.Lukasz // you may not use this file except in compliance with the License.
686e1b661SBorawski.Lukasz // You may obtain a copy of the License at
786e1b661SBorawski.Lukasz //
886e1b661SBorawski.Lukasz //      http://www.apache.org/licenses/LICENSE-2.0
986e1b661SBorawski.Lukasz //
1086e1b661SBorawski.Lukasz // Unless required by applicable law or agreed to in writing, software
1186e1b661SBorawski.Lukasz // distributed under the License is distributed on an "AS IS" BASIS,
1286e1b661SBorawski.Lukasz // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1386e1b661SBorawski.Lukasz // See the License for the specific language governing permissions and
1486e1b661SBorawski.Lukasz // limitations under the License.
1586e1b661SBorawski.Lukasz */
1686e1b661SBorawski.Lukasz #pragma once
1786e1b661SBorawski.Lukasz 
18aecb47a4SBorawski.Lukasz #include <bitset>
19aecb47a4SBorawski.Lukasz #include <cstdint>
20a692779fSEd Tanous #include <vector>
21aecb47a4SBorawski.Lukasz #include "crow.h"
22aecb47a4SBorawski.Lukasz #include <boost/container/flat_map.hpp>
23aecb47a4SBorawski.Lukasz #include <boost/optional.hpp>
24aecb47a4SBorawski.Lukasz 
2586e1b661SBorawski.Lukasz namespace redfish {
2686e1b661SBorawski.Lukasz 
27aecb47a4SBorawski.Lukasz enum class PrivilegeType { BASE, OEM };
28aecb47a4SBorawski.Lukasz 
29a692779fSEd Tanous /** @brief A fixed array of compile time privileges  */
30a692779fSEd Tanous constexpr std::array<const char*, 5> basePrivileges{
313ebd75f7SEd Tanous     "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
323ebd75f7SEd Tanous     "ConfigureUsers"};
3343a095abSBorawski.Lukasz 
34a692779fSEd Tanous constexpr const int basePrivilegeCount = basePrivileges.size();
35a692779fSEd Tanous 
36a692779fSEd Tanous /** @brief Max number of privileges per type  */
37a692779fSEd Tanous constexpr const int MAX_PRIVILEGE_COUNT = 32;
38a692779fSEd Tanous 
39a692779fSEd Tanous /** @brief A vector of all privilege names and their indexes */
40a692779fSEd Tanous static const std::vector<std::string> privilegeNames{basePrivileges.begin(),
41a692779fSEd Tanous                                                      basePrivileges.end()};
4243a095abSBorawski.Lukasz 
4386e1b661SBorawski.Lukasz /**
44aecb47a4SBorawski.Lukasz  * @brief Redfish privileges
45aecb47a4SBorawski.Lukasz  *
46aecb47a4SBorawski.Lukasz  *        Entity privileges and user privileges are represented by this class.
47aecb47a4SBorawski.Lukasz  *
48aecb47a4SBorawski.Lukasz  *        Each incoming connection requires a comparison between privileges held
49aecb47a4SBorawski.Lukasz  *        by the user issuing a request and the target entity's privileges.
50aecb47a4SBorawski.Lukasz  *
51aecb47a4SBorawski.Lukasz  *        To ensure best runtime performance of this comparison, privileges
52aecb47a4SBorawski.Lukasz  *        are represented as bitsets. Each bit in the bitset corresponds to a
53aecb47a4SBorawski.Lukasz  *        unique privilege name.
54aecb47a4SBorawski.Lukasz  *
55aecb47a4SBorawski.Lukasz  *        A bit is set if the privilege is required (entity domain) or granted
56aecb47a4SBorawski.Lukasz  *        (user domain) and false otherwise.
57aecb47a4SBorawski.Lukasz  *
5886e1b661SBorawski.Lukasz  */
59aecb47a4SBorawski.Lukasz class Privileges {
60aecb47a4SBorawski.Lukasz  public:
61aecb47a4SBorawski.Lukasz   /**
6243a095abSBorawski.Lukasz    * @brief Constructs object without any privileges active
6343a095abSBorawski.Lukasz    *
6443a095abSBorawski.Lukasz    */
6543a095abSBorawski.Lukasz   Privileges() = default;
6643a095abSBorawski.Lukasz 
6743a095abSBorawski.Lukasz   /**
6843a095abSBorawski.Lukasz    * @brief Constructs object with given privileges active
6943a095abSBorawski.Lukasz    *
7043a095abSBorawski.Lukasz    * @param[in] privilegeList  List of privileges to be activated
7143a095abSBorawski.Lukasz    *
7243a095abSBorawski.Lukasz    */
733ebd75f7SEd Tanous   Privileges(std::initializer_list<const char*> privilegeList) {
743ebd75f7SEd Tanous     for (const char* privilege : privilegeList) {
753ebd75f7SEd Tanous       if (!setSinglePrivilege(privilege)) {
763ebd75f7SEd Tanous         CROW_LOG_CRITICAL << "Unable to set privilege " << privilege
773ebd75f7SEd Tanous                           << "in constructor";
7843a095abSBorawski.Lukasz       }
7943a095abSBorawski.Lukasz     }
803ebd75f7SEd Tanous   }
81aecb47a4SBorawski.Lukasz 
82aecb47a4SBorawski.Lukasz   /**
83aecb47a4SBorawski.Lukasz    * @brief Sets given privilege in the bitset
84aecb47a4SBorawski.Lukasz    *
85aecb47a4SBorawski.Lukasz    * @param[in] privilege  Privilege to be set
86aecb47a4SBorawski.Lukasz    *
87aecb47a4SBorawski.Lukasz    * @return               None
8843a095abSBorawski.Lukasz    *
89aecb47a4SBorawski.Lukasz    */
903ebd75f7SEd Tanous   bool setSinglePrivilege(const char* privilege) {
91a692779fSEd Tanous     for (int search_index = 0; search_index < privilegeNames.size();
92a692779fSEd Tanous          search_index++) {
93a692779fSEd Tanous       if (privilege == privilegeNames[search_index]) {
94a692779fSEd Tanous         privilegeBitset.set(search_index);
953ebd75f7SEd Tanous         return true;
96aecb47a4SBorawski.Lukasz       }
97a692779fSEd Tanous     }
98aecb47a4SBorawski.Lukasz 
993ebd75f7SEd Tanous     return false;
1003ebd75f7SEd Tanous   }
1013ebd75f7SEd Tanous 
1023ebd75f7SEd Tanous   /**
1033ebd75f7SEd Tanous    * @brief Sets given privilege in the bitset
1043ebd75f7SEd Tanous    *
1053ebd75f7SEd Tanous    * @param[in] privilege  Privilege to be set
1063ebd75f7SEd Tanous    *
1073ebd75f7SEd Tanous    * @return               None
1083ebd75f7SEd Tanous    *
1093ebd75f7SEd Tanous    */
1103ebd75f7SEd Tanous   bool setSinglePrivilege(const std::string& privilege) {
1113ebd75f7SEd Tanous     return setSinglePrivilege(privilege.c_str());
112aecb47a4SBorawski.Lukasz   }
113aecb47a4SBorawski.Lukasz 
114aecb47a4SBorawski.Lukasz   /**
115aecb47a4SBorawski.Lukasz    * @brief Retrieves names of all active privileges for a given type
116aecb47a4SBorawski.Lukasz    *
117aecb47a4SBorawski.Lukasz    * @param[in] type    Base or OEM
118aecb47a4SBorawski.Lukasz    *
1193ebd75f7SEd Tanous    * @return            Vector of active privileges.  Pointers are valid until
120a692779fSEd Tanous    * the setSinglePrivilege is called, or the Privilege structure is destroyed
12143a095abSBorawski.Lukasz    *
122aecb47a4SBorawski.Lukasz    */
1233ebd75f7SEd Tanous   std::vector<const std::string*> getActivePrivilegeNames(
124aecb47a4SBorawski.Lukasz       const PrivilegeType type) const {
1253ebd75f7SEd Tanous     std::vector<const std::string*> activePrivileges;
126aecb47a4SBorawski.Lukasz 
127a692779fSEd Tanous     int search_index = 0;
128a692779fSEd Tanous     int end_index = basePrivilegeCount;
129a692779fSEd Tanous     if (type == PrivilegeType::OEM) {
130a692779fSEd Tanous       search_index = basePrivilegeCount - 1;
131a692779fSEd Tanous       end_index = privilegeNames.size();
132a692779fSEd Tanous     }
133a692779fSEd Tanous 
134a692779fSEd Tanous     for (; search_index < end_index; search_index++) {
135a692779fSEd Tanous       if (privilegeBitset.test(search_index)) {
136a692779fSEd Tanous         activePrivileges.emplace_back(&privilegeNames[search_index]);
137aecb47a4SBorawski.Lukasz       }
138aecb47a4SBorawski.Lukasz     }
139a692779fSEd Tanous 
140aecb47a4SBorawski.Lukasz     return activePrivileges;
141aecb47a4SBorawski.Lukasz   }
142aecb47a4SBorawski.Lukasz 
1433ebd75f7SEd Tanous   /**
1443ebd75f7SEd Tanous    * @brief Determines if this Privilege set is a superset of the given
1453ebd75f7SEd Tanous    * privilege set
1463ebd75f7SEd Tanous    *
1473ebd75f7SEd Tanous    * @param[in] privilege  Privilege to be checked
1483ebd75f7SEd Tanous    *
1493ebd75f7SEd Tanous    * @return               None
1503ebd75f7SEd Tanous    *
1513ebd75f7SEd Tanous    */
1523ebd75f7SEd Tanous   bool isSupersetOf(const Privileges& p) const {
153a692779fSEd Tanous     return (privilegeBitset & p.privilegeBitset) == p.privilegeBitset;
1543ebd75f7SEd Tanous   }
1553ebd75f7SEd Tanous 
15686e1b661SBorawski.Lukasz  private:
157a692779fSEd Tanous   std::bitset<MAX_PRIVILEGE_COUNT> privilegeBitset = 0;
15886e1b661SBorawski.Lukasz };
15986e1b661SBorawski.Lukasz 
160*e0d918bcSEd Tanous using OperationMap = boost::container::flat_map<boost::beast::http::verb,
161*e0d918bcSEd Tanous                                                 std::vector<Privileges>>;
16243a095abSBorawski.Lukasz 
16343a095abSBorawski.Lukasz /**
164aecb47a4SBorawski.Lukasz  * @brief Checks if given privileges allow to call an HTTP method
165aecb47a4SBorawski.Lukasz  *
166aecb47a4SBorawski.Lukasz  * @param[in] method       HTTP method
167aecb47a4SBorawski.Lukasz  * @param[in] user         Privileges
168aecb47a4SBorawski.Lukasz  *
169aecb47a4SBorawski.Lukasz  * @return                 True if method allowed, false otherwise
17043a095abSBorawski.Lukasz  *
171aecb47a4SBorawski.Lukasz  */
172*e0d918bcSEd Tanous inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method,
1733ebd75f7SEd Tanous                                           const OperationMap& operationMap,
1743ebd75f7SEd Tanous                                           const Privileges& userPrivileges) {
1753ebd75f7SEd Tanous   const auto& it = operationMap.find(method);
1763ebd75f7SEd Tanous   if (it == operationMap.end()) {
17743a095abSBorawski.Lukasz     return false;
17843a095abSBorawski.Lukasz   }
179aecb47a4SBorawski.Lukasz 
1803ebd75f7SEd Tanous   // If there are no privileges assigned, assume no privileges required
1813ebd75f7SEd Tanous   if (it->second.empty()) {
18243a095abSBorawski.Lukasz     return true;
18343a095abSBorawski.Lukasz   }
1843ebd75f7SEd Tanous 
1853ebd75f7SEd Tanous   for (auto& requiredPrivileges : it->second) {
1863ebd75f7SEd Tanous     if (userPrivileges.isSupersetOf(requiredPrivileges)) {
1873ebd75f7SEd Tanous       return true;
1883ebd75f7SEd Tanous     }
1893ebd75f7SEd Tanous   }
19043a095abSBorawski.Lukasz   return false;
19186e1b661SBorawski.Lukasz }
192aecb47a4SBorawski.Lukasz 
1933ebd75f7SEd Tanous /**
1943ebd75f7SEd Tanous  * @brief Checks if a user is allowed to call an HTTP method
1953ebd75f7SEd Tanous  *
1963ebd75f7SEd Tanous  * @param[in] method       HTTP method
1973ebd75f7SEd Tanous  * @param[in] user         Username
1983ebd75f7SEd Tanous  *
1993ebd75f7SEd Tanous  * @return                 True if method allowed, false otherwise
2003ebd75f7SEd Tanous  *
2013ebd75f7SEd Tanous  */
202*e0d918bcSEd Tanous inline bool isMethodAllowedForUser(const boost::beast::http::verb method,
2033ebd75f7SEd Tanous                                    const OperationMap& operationMap,
2043ebd75f7SEd Tanous                                    const std::string& user) {
2053ebd75f7SEd Tanous   // TODO: load user privileges from configuration as soon as its available
2063ebd75f7SEd Tanous   // now we are granting all privileges to everyone.
2073ebd75f7SEd Tanous   Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf",
2083ebd75f7SEd Tanous                             "ConfigureUsers", "ConfigureComponents"};
2093ebd75f7SEd Tanous 
2103ebd75f7SEd Tanous   return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges);
21186e1b661SBorawski.Lukasz }
21286e1b661SBorawski.Lukasz 
21386e1b661SBorawski.Lukasz }  // namespace redfish
214