| Borawski.Lukasz | 86e1b66 | 2018-01-19 14:22:14 +0100 | [diff] [blame] | 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 |  | 
| Ed Tanous | c94ad49 | 2019-10-10 15:39:33 -0700 | [diff] [blame] | 18 | #include <logging.h> | 
| Tanous | f00032d | 2018-11-05 01:18:10 -0300 | [diff] [blame] | 19 |  | 
 | 20 | #include <array> | 
| Borawski.Lukasz | aecb47a | 2018-01-25 12:14:14 +0100 | [diff] [blame] | 21 | #include <bitset> | 
| Tanous | f00032d | 2018-11-05 01:18:10 -0300 | [diff] [blame] | 22 | #include <boost/beast/http/verb.hpp> | 
| Borawski.Lukasz | aecb47a | 2018-01-25 12:14:14 +0100 | [diff] [blame] | 23 | #include <boost/container/flat_map.hpp> | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 24 | #include <cstdint> | 
 | 25 | #include <vector> | 
| Borawski.Lukasz | aecb47a | 2018-01-25 12:14:14 +0100 | [diff] [blame] | 26 |  | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 27 | namespace redfish | 
 | 28 | { | 
 | 29 |  | 
 | 30 | enum class PrivilegeType | 
 | 31 | { | 
 | 32 |     BASE, | 
 | 33 |     OEM | 
 | 34 | }; | 
| Borawski.Lukasz | aecb47a | 2018-01-25 12:14:14 +0100 | [diff] [blame] | 35 |  | 
| Ed Tanous | a692779 | 2018-03-06 10:01:57 -0800 | [diff] [blame] | 36 | /** @brief A fixed array of compile time privileges  */ | 
 | 37 | constexpr std::array<const char*, 5> basePrivileges{ | 
| Ed Tanous | 3ebd75f | 2018-03-05 18:20:01 -0800 | [diff] [blame] | 38 |     "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf", | 
 | 39 |     "ConfigureUsers"}; | 
| Borawski.Lukasz | 43a095a | 2018-02-19 15:39:01 +0100 | [diff] [blame] | 40 |  | 
| Ed Tanous | 271584a | 2019-07-09 16:24:22 -0700 | [diff] [blame] | 41 | constexpr const size_t basePrivilegeCount = basePrivileges.size(); | 
| Ed Tanous | a692779 | 2018-03-06 10:01:57 -0800 | [diff] [blame] | 42 |  | 
 | 43 | /** @brief Max number of privileges per type  */ | 
| Ed Tanous | 271584a | 2019-07-09 16:24:22 -0700 | [diff] [blame] | 44 | constexpr const size_t maxPrivilegeCount = 32; | 
| Ed Tanous | a692779 | 2018-03-06 10:01:57 -0800 | [diff] [blame] | 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()}; | 
| Borawski.Lukasz | 43a095a | 2018-02-19 15:39:01 +0100 | [diff] [blame] | 49 |  | 
| Borawski.Lukasz | 86e1b66 | 2018-01-19 14:22:14 +0100 | [diff] [blame] | 50 | /** | 
| Borawski.Lukasz | aecb47a | 2018-01-25 12:14:14 +0100 | [diff] [blame] | 51 |  * @brief Redfish privileges | 
 | 52 |  * | 
| Joseph Reynolds | 900f949 | 2019-11-25 15:37:29 -0600 | [diff] [blame] | 53 |  *        This implements a set of Redfish privileges.  These directly represent | 
 | 54 |  *        user privileges and help represent entity privileges. | 
| Borawski.Lukasz | aecb47a | 2018-01-25 12:14:14 +0100 | [diff] [blame] | 55 |  * | 
| Ed Tanous | 55c7b7a | 2018-05-22 15:27:24 -0700 | [diff] [blame] | 56 |  *        Each incoming Connection requires a comparison between privileges held | 
| Borawski.Lukasz | aecb47a | 2018-01-25 12:14:14 +0100 | [diff] [blame] | 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 |  * | 
| Borawski.Lukasz | aecb47a | 2018-01-25 12:14:14 +0100 | [diff] [blame] | 63 |  *        A bit is set if the privilege is required (entity domain) or granted | 
 | 64 |  *        (user domain) and false otherwise. | 
 | 65 |  * | 
| Borawski.Lukasz | 86e1b66 | 2018-01-19 14:22:14 +0100 | [diff] [blame] | 66 |  */ | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 67 | class Privileges | 
 | 68 | { | 
 | 69 |   public: | 
 | 70 |     /** | 
 | 71 |      * @brief Constructs object without any privileges active | 
 | 72 |      * | 
 | 73 |      */ | 
 | 74 |     Privileges() = default; | 
| Borawski.Lukasz | 43a095a | 2018-02-19 15:39:01 +0100 | [diff] [blame] | 75 |  | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 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 |         } | 
| Borawski.Lukasz | aecb47a | 2018-01-25 12:14:14 +0100 | [diff] [blame] | 92 |     } | 
 | 93 |  | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 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 |     { | 
| Ed Tanous | 271584a | 2019-07-09 16:24:22 -0700 | [diff] [blame] | 104 |         for (size_t searchIndex = 0; searchIndex < privilegeNames.size(); | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 105 |              searchIndex++) | 
 | 106 |         { | 
 | 107 |             if (privilege == privilegeNames[searchIndex]) | 
 | 108 |             { | 
 | 109 |                 privilegeBitset.set(searchIndex); | 
 | 110 |                 return true; | 
 | 111 |             } | 
 | 112 |         } | 
| Ed Tanous | 3ebd75f | 2018-03-05 18:20:01 -0800 | [diff] [blame] | 113 |  | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 114 |         return false; | 
| Ed Tanous | a692779 | 2018-03-06 10:01:57 -0800 | [diff] [blame] | 115 |     } | 
 | 116 |  | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 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()); | 
| Borawski.Lukasz | aecb47a | 2018-01-25 12:14:14 +0100 | [diff] [blame] | 128 |     } | 
| Ed Tanous | a692779 | 2018-03-06 10:01:57 -0800 | [diff] [blame] | 129 |  | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 130 |     /** | 
| Joseph Reynolds | 900f949 | 2019-11-25 15:37:29 -0600 | [diff] [blame] | 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 |     /** | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 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; | 
| Borawski.Lukasz | aecb47a | 2018-01-25 12:14:14 +0100 | [diff] [blame] | 165 |  | 
| Ed Tanous | 271584a | 2019-07-09 16:24:22 -0700 | [diff] [blame] | 166 |         size_t searchIndex = 0; | 
 | 167 |         size_t endIndex = basePrivilegeCount; | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 168 |         if (type == PrivilegeType::OEM) | 
 | 169 |         { | 
 | 170 |             searchIndex = basePrivilegeCount - 1; | 
 | 171 |             endIndex = privilegeNames.size(); | 
 | 172 |         } | 
| Ed Tanous | 3ebd75f | 2018-03-05 18:20:01 -0800 | [diff] [blame] | 173 |  | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 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 |   private: | 
 | 200 |     std::bitset<maxPrivilegeCount> privilegeBitset = 0; | 
| Borawski.Lukasz | 86e1b66 | 2018-01-19 14:22:14 +0100 | [diff] [blame] | 201 | }; | 
 | 202 |  | 
| Ratan Gupta | 6f35956 | 2019-04-03 10:39:08 +0530 | [diff] [blame] | 203 | inline const Privileges& getUserPrivileges(const std::string& userRole) | 
 | 204 | { | 
 | 205 |     // Redfish privilege : Administrator | 
 | 206 |     if (userRole == "priv-admin") | 
 | 207 |     { | 
 | 208 |         static Privileges admin{"Login", "ConfigureManager", "ConfigureSelf", | 
 | 209 |                                 "ConfigureUsers", "ConfigureComponents"}; | 
 | 210 |         return admin; | 
 | 211 |     } | 
 | 212 |     else if (userRole == "priv-operator") | 
 | 213 |     { | 
 | 214 |         // Redfish privilege : Operator | 
 | 215 |         static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"}; | 
 | 216 |         return op; | 
 | 217 |     } | 
| jayaprakash Mutyala | d7e0802 | 2019-12-05 23:29:13 +0000 | [diff] [blame] | 218 |     else if (userRole == "priv-user") | 
| Ratan Gupta | 6f35956 | 2019-04-03 10:39:08 +0530 | [diff] [blame] | 219 |     { | 
 | 220 |         // Redfish privilege : Readonly | 
 | 221 |         static Privileges readOnly{"Login", "ConfigureSelf"}; | 
 | 222 |         return readOnly; | 
 | 223 |     } | 
| jayaprakash Mutyala | d7e0802 | 2019-12-05 23:29:13 +0000 | [diff] [blame] | 224 |     else | 
 | 225 |     { | 
 | 226 |         // Redfish privilege : NoAccess | 
 | 227 |         static Privileges noaccess; | 
 | 228 |         return noaccess; | 
 | 229 |     } | 
| Ratan Gupta | 6f35956 | 2019-04-03 10:39:08 +0530 | [diff] [blame] | 230 | } | 
 | 231 |  | 
| Joseph Reynolds | 900f949 | 2019-11-25 15:37:29 -0600 | [diff] [blame] | 232 | /** | 
 | 233 |  * @brief The OperationMap represents the privileges required for a | 
 | 234 |  * single entity (URI).  It maps from the allowable verbs to the | 
 | 235 |  * privileges required to use that operation. | 
 | 236 |  * | 
 | 237 |  * This represents the Redfish "Privilege AND and OR syntax" as given | 
 | 238 |  * in the spec and shown in the Privilege Registry.  This does not | 
 | 239 |  * implement any Redfish property overrides, subordinate overrides, or | 
 | 240 |  * resource URI overrides.  This does not implement the limitation of | 
 | 241 |  * the ConfigureSelf privilege to operate only on your own account or | 
 | 242 |  * session. | 
 | 243 |  **/ | 
| Ed Tanous | e0d918b | 2018-03-27 17:41:04 -0700 | [diff] [blame] | 244 | using OperationMap = boost::container::flat_map<boost::beast::http::verb, | 
 | 245 |                                                 std::vector<Privileges>>; | 
| Borawski.Lukasz | 43a095a | 2018-02-19 15:39:01 +0100 | [diff] [blame] | 246 |  | 
| Joseph Reynolds | 900f949 | 2019-11-25 15:37:29 -0600 | [diff] [blame] | 247 | /* @brief Checks if user is allowed to call an operation | 
 | 248 |  * | 
 | 249 |  * @param[in] operationPrivilegesRequired   Privileges required | 
 | 250 |  * @param[in] userPrivileges                Privileges the user has | 
 | 251 |  * | 
 | 252 |  * @return                 True if operation is allowed, false otherwise | 
 | 253 |  */ | 
 | 254 | inline bool isOperationAllowedWithPrivileges( | 
 | 255 |     const std::vector<Privileges>& operationPrivilegesRequired, | 
 | 256 |     const Privileges& userPrivileges) | 
 | 257 | { | 
 | 258 |     // If there are no privileges assigned, there are no privileges required | 
 | 259 |     if (operationPrivilegesRequired.empty()) | 
 | 260 |     { | 
 | 261 |         return true; | 
 | 262 |     } | 
 | 263 |     for (auto& requiredPrivileges : operationPrivilegesRequired) | 
 | 264 |     { | 
 | 265 |         BMCWEB_LOG_ERROR << "Checking operation privileges..."; | 
 | 266 |         if (userPrivileges.isSupersetOf(requiredPrivileges)) | 
 | 267 |         { | 
 | 268 |             BMCWEB_LOG_ERROR << "...success"; | 
 | 269 |             return true; | 
 | 270 |         } | 
 | 271 |     } | 
 | 272 |     return false; | 
 | 273 | } | 
 | 274 |  | 
| Borawski.Lukasz | 43a095a | 2018-02-19 15:39:01 +0100 | [diff] [blame] | 275 | /** | 
| Ed Tanous | 3ebd75f | 2018-03-05 18:20:01 -0800 | [diff] [blame] | 276 |  * @brief Checks if given privileges allow to call an HTTP method | 
 | 277 |  * | 
 | 278 |  * @param[in] method       HTTP method | 
 | 279 |  * @param[in] user         Privileges | 
 | 280 |  * | 
 | 281 |  * @return                 True if method allowed, false otherwise | 
| Borawski.Lukasz | 43a095a | 2018-02-19 15:39:01 +0100 | [diff] [blame] | 282 |  * | 
 | 283 |  */ | 
| Ed Tanous | e0d918b | 2018-03-27 17:41:04 -0700 | [diff] [blame] | 284 | inline bool isMethodAllowedWithPrivileges(const boost::beast::http::verb method, | 
| Ed Tanous | 3ebd75f | 2018-03-05 18:20:01 -0800 | [diff] [blame] | 285 |                                           const OperationMap& operationMap, | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 286 |                                           const Privileges& userPrivileges) | 
 | 287 | { | 
 | 288 |     const auto& it = operationMap.find(method); | 
 | 289 |     if (it == operationMap.end()) | 
 | 290 |     { | 
 | 291 |         return false; | 
| Ed Tanous | 3ebd75f | 2018-03-05 18:20:01 -0800 | [diff] [blame] | 292 |     } | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 293 |  | 
| Joseph Reynolds | 900f949 | 2019-11-25 15:37:29 -0600 | [diff] [blame] | 294 |     return isOperationAllowedWithPrivileges(it->second, userPrivileges); | 
| Ed Tanous | 3ebd75f | 2018-03-05 18:20:01 -0800 | [diff] [blame] | 295 | } | 
| Borawski.Lukasz | aecb47a | 2018-01-25 12:14:14 +0100 | [diff] [blame] | 296 |  | 
| Ed Tanous | 3ebd75f | 2018-03-05 18:20:01 -0800 | [diff] [blame] | 297 | /** | 
 | 298 |  * @brief Checks if a user is allowed to call an HTTP method | 
 | 299 |  * | 
 | 300 |  * @param[in] method       HTTP method | 
 | 301 |  * @param[in] user         Username | 
 | 302 |  * | 
 | 303 |  * @return                 True if method allowed, false otherwise | 
 | 304 |  * | 
 | 305 |  */ | 
| Ed Tanous | e0d918b | 2018-03-27 17:41:04 -0700 | [diff] [blame] | 306 | inline bool isMethodAllowedForUser(const boost::beast::http::verb method, | 
| Ed Tanous | 3ebd75f | 2018-03-05 18:20:01 -0800 | [diff] [blame] | 307 |                                    const OperationMap& operationMap, | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 308 |                                    const std::string& user) | 
 | 309 | { | 
 | 310 |     // TODO: load user privileges from configuration as soon as its available | 
 | 311 |     // now we are granting all privileges to everyone. | 
 | 312 |     Privileges userPrivileges{"Login", "ConfigureManager", "ConfigureSelf", | 
 | 313 |                               "ConfigureUsers", "ConfigureComponents"}; | 
| Ed Tanous | 3ebd75f | 2018-03-05 18:20:01 -0800 | [diff] [blame] | 314 |  | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 315 |     return isMethodAllowedWithPrivileges(method, operationMap, userPrivileges); | 
| Ed Tanous | 3ebd75f | 2018-03-05 18:20:01 -0800 | [diff] [blame] | 316 | } | 
| Borawski.Lukasz | 86e1b66 | 2018-01-19 14:22:14 +0100 | [diff] [blame] | 317 |  | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 318 | } // namespace redfish |