Borawski.Lukasz | aecb47a | 2018-01-25 12:14: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 | #include "privileges.hpp" |
| 17 | |
| 18 | namespace redfish { |
| 19 | |
| 20 | boost::container::flat_map<std::string, size_t> |
| 21 | Privileges::basePrivNameToIndexMap; |
| 22 | boost::container::flat_map<std::string, size_t> |
| 23 | Privileges::oemPrivNameToIndexMap; |
| 24 | |
| 25 | bool EntityPrivileges::isMethodAllowedForUser(const crow::HTTPMethod method, |
| 26 | const std::string& user) const { |
| 27 | // TODO: load user privileges from configuration as soon as its available |
| 28 | // now we are granting only Login. |
| 29 | auto userPrivileges = Privileges(); |
| 30 | userPrivileges.setSinglePrivilege("Login"); |
| 31 | |
| 32 | return isMethodAllowedWithPrivileges(method, userPrivileges); |
| 33 | } |
| 34 | |
| 35 | bool EntityPrivileges::isMethodAllowedWithPrivileges( |
| 36 | const crow::HTTPMethod method, const Privileges& userPrivileges) const { |
| 37 | if (methodToPrivilegeMap.find(method) == methodToPrivilegeMap.end()) { |
| 38 | return false; |
| 39 | } |
| 40 | |
| 41 | for (auto& requiredPrivileges : methodToPrivilegeMap.at(method)) { |
| 42 | // Check if user has required base privileges |
| 43 | if (!verifyPrivileges(userPrivileges.getBasePrivilegeBitset(), |
| 44 | requiredPrivileges.getBasePrivilegeBitset())) { |
| 45 | continue; |
| 46 | } |
| 47 | |
| 48 | // Check if user has required OEM privileges |
| 49 | if (!verifyPrivileges(userPrivileges.getOEMPrivilegeBitset(), |
| 50 | requiredPrivileges.getOEMPrivilegeBitset())) { |
| 51 | continue; |
| 52 | } |
| 53 | |
| 54 | return true; |
| 55 | } |
| 56 | return false; |
| 57 | } |
| 58 | |
| 59 | bool EntityPrivileges::verifyPrivileges( |
| 60 | const privilegeBitset userPrivilegeBitset, |
| 61 | const privilegeBitset requiredPrivilegeBitset) const { |
| 62 | return (userPrivilegeBitset & requiredPrivilegeBitset) == |
| 63 | requiredPrivilegeBitset; |
| 64 | } |
| 65 | |
| 66 | EntityPrivileges PrivilegeProvider::getPrivilegesRequiredByEntity( |
| 67 | const std::string& entityUrl, const std::string& entityType) const { |
| 68 | if (privilegeRegistryJson.empty()) { |
| 69 | return EntityPrivileges(); |
| 70 | } |
| 71 | |
| 72 | // type from @odata.type e.g: ServiceRoot from #ServiceRoot.v1_1_1.ServiceRoot |
| 73 | auto entity = entityType.substr(entityType.find_last_of(".") + strlen(".")); |
| 74 | |
| 75 | for (auto mapping : privilegeRegistryJson.at("Mappings")) { |
| 76 | const auto& entityJson = mapping.find("Entity"); |
| 77 | const auto& operationMapJson = mapping.find("OperationMap"); |
| 78 | const auto& propertyOverridesJson = mapping.find("PropertyOverrides"); |
| 79 | const auto& subordinateOverridesJson = mapping.find("SubordinateOverrides"); |
| 80 | const auto& resourceURIOverridesJson = mapping.find("ResourceURIOverrides"); |
| 81 | |
| 82 | if (entityJson == mapping.end() || operationMapJson == mapping.end()) { |
| 83 | return EntityPrivileges(); |
| 84 | } |
| 85 | |
| 86 | if (entityJson->is_string() && entity == entityJson.value()) { |
| 87 | auto entityPrivileges = EntityPrivileges(); |
| 88 | |
| 89 | if (!parseOperationMap(operationMapJson.value(), entityPrivileges)) { |
| 90 | return EntityPrivileges(); |
| 91 | } |
| 92 | |
| 93 | if (propertyOverridesJson != mapping.end()) { |
| 94 | // TODO: implementation comes in next patch-sets |
| 95 | } |
| 96 | if (subordinateOverridesJson != mapping.end()) { |
| 97 | // TODO: implementation comes in next patch-sets |
| 98 | } |
| 99 | if (resourceURIOverridesJson != mapping.end()) { |
| 100 | // TODO: implementation comes in next patch-sets |
| 101 | } |
| 102 | |
| 103 | return entityPrivileges; |
| 104 | } |
| 105 | } |
| 106 | return EntityPrivileges(); |
| 107 | } |
| 108 | |
| 109 | bool PrivilegeProvider::parseOperationMap( |
| 110 | const nlohmann::json& operationMap, |
| 111 | EntityPrivileges& entityPrivileges) const { |
| 112 | for (auto it = operationMap.begin(); it != operationMap.end(); ++it) { |
| 113 | const std::string& method = it.key(); |
| 114 | const nlohmann::json& privilegesForMethod = it.value(); |
| 115 | |
| 116 | for (const auto& privilegeOr : privilegesForMethod) { |
| 117 | const auto& privilegeJson = privilegeOr.find("Privilege"); |
| 118 | |
| 119 | if (privilegeJson == privilegeOr.end()) { |
| 120 | return false; |
| 121 | } |
| 122 | auto privileges = Privileges(); |
| 123 | |
| 124 | for (auto& privilegeAnd : privilegeJson.value()) { |
| 125 | if (!privilegeAnd.is_string()) { |
| 126 | return false; |
| 127 | } |
| 128 | privileges.setSinglePrivilege(privilegeAnd); |
| 129 | } |
| 130 | entityPrivileges.addPrivilegesRequiredByMethod(operator"" _method( |
| 131 | method.c_str(), |
| 132 | method.size()), |
| 133 | privileges); |
| 134 | } |
| 135 | } |
| 136 | return true; |
| 137 | } |
| 138 | |
| 139 | bool PrivilegeProvider::loadPrivilegesFromFile( |
| 140 | std::ifstream& privilegeRegistryFile) { |
| 141 | privilegeRegistryJson = |
| 142 | nlohmann::json::parse(privilegeRegistryFile, nullptr, false); |
| 143 | |
| 144 | if (!privilegeRegistryHasRequiredFields()) { |
| 145 | return false; |
| 146 | } |
| 147 | |
| 148 | const nlohmann::json& basePrivilegesUsed = |
| 149 | privilegeRegistryJson.at("PrivilegesUsed"); |
| 150 | if (basePrivilegesUsed.size() == 0) { |
| 151 | return false; |
| 152 | } |
| 153 | if (!fillPrivilegeMap(basePrivilegesUsed, |
| 154 | Privileges::basePrivNameToIndexMap)) { |
| 155 | return false; |
| 156 | } |
| 157 | |
| 158 | const nlohmann::json& oemPrivilegesUsed = |
| 159 | privilegeRegistryJson.at("OEMPrivilegesUsed"); |
| 160 | if (!fillPrivilegeMap(oemPrivilegesUsed, Privileges::oemPrivNameToIndexMap)) { |
| 161 | return false; |
| 162 | } |
| 163 | |
| 164 | return true; |
| 165 | } |
| 166 | |
| 167 | bool PrivilegeProvider::privilegeRegistryHasRequiredFields() const { |
| 168 | if (privilegeRegistryJson.is_discarded() || |
| 169 | privilegeRegistryJson.find("@Redfish.Copyright") == |
| 170 | privilegeRegistryJson.end() || |
| 171 | privilegeRegistryJson.find("@odata.type") == |
| 172 | privilegeRegistryJson.end() || |
| 173 | privilegeRegistryJson.find("Id") == privilegeRegistryJson.end() || |
| 174 | privilegeRegistryJson.find("Name") == privilegeRegistryJson.end() || |
| 175 | privilegeRegistryJson.find("Mappings") == privilegeRegistryJson.end() || |
| 176 | privilegeRegistryJson.find("PrivilegesUsed") == |
| 177 | privilegeRegistryJson.end() || |
| 178 | privilegeRegistryJson.find("OEMPrivilegesUsed") == |
| 179 | privilegeRegistryJson.end()) { |
| 180 | return false; |
| 181 | } |
| 182 | return true; |
| 183 | } |
| 184 | |
| 185 | bool PrivilegeProvider::fillPrivilegeMap( |
| 186 | const nlohmann::json& privilegesUsed, |
| 187 | boost::container::flat_map<std::string, size_t>& privilegeToIndexMap) |
| 188 | const { |
| 189 | privilegeToIndexMap.clear(); |
| 190 | for (auto& privilege : privilegesUsed) { |
| 191 | if (privilegeToIndexMap.size() < MAX_PRIVILEGE_COUNT) { |
| 192 | if (!privilege.is_string()) { |
| 193 | return false; |
| 194 | } |
| 195 | privilegeToIndexMap.insert(std::pair<std::string, size_t>( |
| 196 | privilege.get<std::string>(), privilegeToIndexMap.size())); |
| 197 | } |
| 198 | } |
| 199 | return true; |
| 200 | } |
| 201 | |
| 202 | } // namespace redfish |