blob: 2cc52b5b5c235910a693c852704da7fdd3219ae6 [file] [log] [blame]
/*
// Copyright (c) 2018 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#pragma once
#include <bitset>
#include <cstdint>
#include "crow.h"
#include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp>
#include <boost/optional.hpp>
namespace redfish {
class PrivilegeProvider;
enum class PrivilegeType { BASE, OEM };
/** @brief Max number of privileges per type */
constexpr const size_t MAX_PRIVILEGE_COUNT = 32;
using privilegeBitset = std::bitset<MAX_PRIVILEGE_COUNT>;
/** @brief Number of mappings must be <= MAX_PRIVILEGE_COUNT */
static const boost::container::flat_map<std::string, size_t>
basePrivNameToIndexMap = {{"Login", 0},
{"ConfigureManager", 1},
{"ConfigureComponents", 2},
{"ConfigureSelf", 3},
{"ConfigureUsers", 4}};
/** @brief Number of mappings must be <= MAX_PRIVILEGE_COUNT */
static const boost::container::flat_map<std::string, size_t>
oemPrivNameToIndexMap = {};
/**
* @brief Redfish privileges
*
* Entity privileges and user privileges are represented by this class.
*
* Each incoming connection requires a comparison between privileges held
* by the user issuing a request and the target entity's privileges.
*
* To ensure best runtime performance of this comparison, privileges
* are represented as bitsets. Each bit in the bitset corresponds to a
* unique privilege name.
*
* A bit is set if the privilege is required (entity domain) or granted
* (user domain) and false otherwise.
*
*/
class Privileges {
public:
/**
* @brief Constructs object without any privileges active
*
*/
Privileges() = default;
/**
* @brief Constructs object with given privileges active
*
* @param[in] privilegeList List of privileges to be activated
*
*/
Privileges(std::initializer_list<std::string> privilegeList) {
for (const auto& privilege : privilegeList) {
setSinglePrivilege(privilege);
}
}
/**
* @brief Retrieves the base privileges bitset
*
* @return Bitset representation of base Redfish privileges
*
*/
privilegeBitset getBasePrivilegeBitset() const { return basePrivilegeBitset; }
/**
* @brief Retrieves the OEM privileges bitset
*
* @return Bitset representation of OEM Redfish privileges
*
*/
privilegeBitset getOEMPrivilegeBitset() const { return oemPrivilegeBitset; }
/**
* @brief Sets given privilege in the bitset
*
* @param[in] privilege Privilege to be set
*
* @return None
*
*/
void setSinglePrivilege(const std::string& privilege) {
auto index = getBitsetIndexForPrivilege(privilege, PrivilegeType::BASE);
if (index) {
basePrivilegeBitset.set(*index);
return;
}
index = getBitsetIndexForPrivilege(privilege, PrivilegeType::OEM);
if (index) {
oemPrivilegeBitset.set(*index);
}
}
/**
* @brief Retrieves names of all active privileges for a given type
*
* @param[in] type Base or OEM
*
* @return Vector of active privileges
*
*/
std::vector<std::string> getActivePrivilegeNames(
const PrivilegeType type) const {
std::vector<std::string> activePrivileges;
if (type == PrivilegeType::BASE) {
for (const auto& pair : basePrivNameToIndexMap) {
if (basePrivilegeBitset.test(pair.second)) {
activePrivileges.emplace_back(pair.first);
}
}
} else {
for (const auto& pair : oemPrivNameToIndexMap) {
if (oemPrivilegeBitset.test(pair.second)) {
activePrivileges.emplace_back(pair.first);
}
}
}
return activePrivileges;
}
private:
boost::optional<size_t> getBitsetIndexForPrivilege(
const std::string& privilege, const PrivilegeType type) const {
if (type == PrivilegeType::BASE) {
const auto pair = basePrivNameToIndexMap.find(privilege);
if (pair != basePrivNameToIndexMap.end()) {
return pair->second;
}
} else {
const auto pair = oemPrivNameToIndexMap.find(privilege);
if (pair != oemPrivNameToIndexMap.end()) {
return pair->second;
}
}
return boost::none;
}
privilegeBitset basePrivilegeBitset;
privilegeBitset oemPrivilegeBitset;
friend class PrivilegeProvider;
};
using OperationMap =
boost::container::flat_map<crow::HTTPMethod, std::vector<Privileges>>;
/**
* @brief Class used to store overrides privileges for Redfish
* entities
*
*/
class EntityPrivilegesOverride {
protected:
/**
* @brief Constructs overrides object for given targets
*
* @param[in] operationMap Operation map to be applied for targets
* @param[in] targets List of targets whOperation map to be applied for
* targets
*
*/
EntityPrivilegesOverride(OperationMap&& operationMap,
std::initializer_list<std::string>&& targets)
: operationMap(std::move(operationMap)), targets(std::move(targets)) {}
const OperationMap operationMap;
const boost::container::flat_set<std::string> targets;
};
class PropertyOverride : public EntityPrivilegesOverride {
public:
PropertyOverride(OperationMap&& operationMap,
std::initializer_list<std::string>&& targets)
: EntityPrivilegesOverride(std::move(operationMap), std::move(targets)) {}
};
class SubordinateOverride : public EntityPrivilegesOverride {
public:
SubordinateOverride(OperationMap&& operationMap,
std::initializer_list<std::string>&& targets)
: EntityPrivilegesOverride(std::move(operationMap), std::move(targets)) {}
};
class ResourceURIOverride : public EntityPrivilegesOverride {
public:
ResourceURIOverride(OperationMap&& operationMap,
std::initializer_list<std::string>&& targets)
: EntityPrivilegesOverride(std::move(operationMap), std::move(targets)) {}
};
/**
* @brief Class used to store privileges for Redfish entities
*
*/
class EntityPrivileges {
public:
/**
* @brief Constructor for default case with no overrides
*
* @param[in] operationMap Operation map for the entity
*
*/
EntityPrivileges(OperationMap&& operationMap)
: operationMap(std::move(operationMap)) {}
/**
* @brief Constructors for overrides
*
* @param[in] operationMap Default operation map for the entity
* @param[in] propertyOverrides Vector of property overrides
* @param[in] subordinateOverrides Vector of subordinate overrides
* @param[in] resourceURIOverrides Vector of resource URI overrides
*
*/
EntityPrivileges(OperationMap&& operationMap,
std::vector<PropertyOverride>&& propertyOverrides,
std::vector<SubordinateOverride>&& subordinateOverrides,
std::vector<ResourceURIOverride>&& resourceURIOverrides)
: operationMap(std::move(operationMap)),
propertyOverrides(std::move(propertyOverrides)),
subordinateOverrides(std::move(subordinateOverrides)),
resourceURIOverrides(std::move(resourceURIOverrides)) {}
/**
* @brief Checks if a user is allowed to call an HTTP method
*
* @param[in] method HTTP method
* @param[in] user Username
*
* @return True if method allowed, false otherwise
*
*/
bool isMethodAllowedForUser(const crow::HTTPMethod method,
const std::string& user) const {
// TODO: load user privileges from configuration as soon as its available
// now we are granting all privileges to everyone.
auto userPrivileges =
Privileges{"Login", "ConfigureManager", "ConfigureSelf",
"ConfigureUsers", "ConfigureComponents"};
return isMethodAllowedWithPrivileges(method, userPrivileges);
}
/**
* @brief Checks if given privileges allow to call an HTTP method
*
* @param[in] method HTTP method
* @param[in] user Privileges
*
* @return True if method allowed, false otherwise
*
*/
bool isMethodAllowedWithPrivileges(const crow::HTTPMethod method,
const Privileges& userPrivileges) const {
if (operationMap.find(method) == operationMap.end()) {
return false;
}
for (auto& requiredPrivileges : operationMap.at(method)) {
// Check if user has required base privileges
if (!verifyPrivileges(userPrivileges.getBasePrivilegeBitset(),
requiredPrivileges.getBasePrivilegeBitset())) {
continue;
}
// Check if user has required OEM privileges
if (!verifyPrivileges(userPrivileges.getOEMPrivilegeBitset(),
requiredPrivileges.getOEMPrivilegeBitset())) {
continue;
}
return true;
}
return false;
}
private:
bool verifyPrivileges(const privilegeBitset userPrivilegeBitset,
const privilegeBitset requiredPrivilegeBitset) const {
return (userPrivilegeBitset & requiredPrivilegeBitset) ==
requiredPrivilegeBitset;
}
OperationMap operationMap;
// Overrides are not implemented at the moment.
std::vector<PropertyOverride> propertyOverrides;
std::vector<SubordinateOverride> subordinateOverrides;
std::vector<ResourceURIOverride> resourceURIOverrides;
};
} // namespace redfish