blob: b574c4e7af85e5c05a24a9a521421d7a6367d769 [file] [log] [blame]
#pragma once
#include "dbus_utility.hpp"
#include "logging.hpp"
#include "utils/dbus_utils.hpp"
#include <boost/container/flat_map.hpp>
#include <boost/url/format.hpp>
#include <sdbusplus/bus/match.hpp>
#include <sdbusplus/unpack_properties.hpp>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <variant>
#include <vector>
namespace crow
{
struct UserFields
{
std::optional<std::string> userRole;
std::optional<bool> remote;
std::optional<bool> passwordExpired;
std::optional<std::vector<std::string>> userGroups;
};
struct UserRoleMap
{
public:
static UserRoleMap& getInstance()
{
static UserRoleMap userRoleMap;
return userRoleMap;
}
UserFields getUserRole(std::string_view name)
{
auto it = roleMap.find(name);
if (it == roleMap.end())
{
BMCWEB_LOG_ERROR("User name {} is not found in the UserRoleMap.",
name);
return {};
}
return it->second;
}
UserRoleMap(const UserRoleMap&) = delete;
UserRoleMap& operator=(const UserRoleMap&) = delete;
UserRoleMap(UserRoleMap&&) = delete;
UserRoleMap& operator=(UserRoleMap&&) = delete;
~UserRoleMap() = default;
private:
static UserFields extractUserRole(
const dbus::utility::DBusInteracesMap& interfacesProperties)
{
UserFields fields;
for (const auto& interface : interfacesProperties)
{
for (const auto& property : interface.second)
{
if (property.first == "UserPrivilege")
{
const std::string* role =
std::get_if<std::string>(&property.second);
if (role != nullptr)
{
fields.userRole = *role;
}
}
else if (property.first == "UserGroups")
{
const std::vector<std::string>* groups =
std::get_if<std::vector<std::string>>(&property.second);
if (groups != nullptr)
{
fields.userGroups = *groups;
}
}
else if (property.first == "UserPasswordExpired")
{
const bool* expired = std::get_if<bool>(&property.second);
if (expired != nullptr)
{
fields.passwordExpired = *expired;
}
}
else if (property.first == "RemoteUser")
{
const bool* remote = std::get_if<bool>(&property.second);
if (remote != nullptr)
{
fields.remote = *remote;
}
}
}
}
return fields;
}
void userAdded(sdbusplus::message_t& m)
{
BMCWEB_LOG_DEBUG("User Added");
sdbusplus::message::object_path objPath;
dbus::utility::DBusInteracesMap interfacesProperties;
try
{
m.read(objPath, interfacesProperties);
}
catch (const sdbusplus::exception::SdBusError& e)
{
BMCWEB_LOG_ERROR(
"Failed to parse user add signal.ERROR={}REPLY_SIG={}",
e.what(), m.get_signature());
return;
}
BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
std::string name = objPath.filename();
if (name.empty())
{
return;
}
UserFields role = extractUserRole(interfacesProperties);
// Insert the newly added user name and the role
auto res = roleMap.emplace(name, role);
if (!res.second)
{
BMCWEB_LOG_ERROR(
"Insertion of the user=\"{}\" in the roleMap failed.", name);
return;
}
}
void userRemoved(sdbusplus::message_t& m)
{
BMCWEB_LOG_DEBUG("User Removed");
sdbusplus::message::object_path objPath;
try
{
m.read(objPath);
}
catch (const sdbusplus::exception::SdBusError& e)
{
BMCWEB_LOG_ERROR("Failed to parse user delete signal.");
BMCWEB_LOG_ERROR("ERROR={}REPLY_SIG={}", e.what(),
m.get_signature());
return;
}
BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
std::string name = objPath.filename();
if (name.empty())
{
return;
}
roleMap.erase(name);
}
void userPropertiesChanged(sdbusplus::message_t& m)
{
BMCWEB_LOG_DEBUG("Properties Changed");
std::string interface;
dbus::utility::DBusPropertiesMap changedProperties;
try
{
m.read(interface, changedProperties);
}
catch (const sdbusplus::exception::SdBusError& e)
{
BMCWEB_LOG_ERROR("Failed to parse user properties changed signal.");
BMCWEB_LOG_ERROR("ERROR={}REPLY_SIG={}", e.what(),
m.get_signature());
return;
}
dbus::utility::DBusInteracesMap map;
map.emplace_back("xyz.openbmc_project.User.Attributes",
changedProperties);
const sdbusplus::message::object_path path(m.get_path());
BMCWEB_LOG_DEBUG("Object Path = \"{}\"", path.str);
std::string user = path.filename();
if (user.empty())
{
return;
}
BMCWEB_LOG_DEBUG("User Name = \"{}\"", user);
UserFields role = extractUserRole(map);
auto userProps = roleMap.find(user);
if (userProps == roleMap.end())
{
BMCWEB_LOG_CRITICAL("User {} not found", user);
return;
}
if (role.userRole)
{
userProps->second.userRole = role.userRole;
}
if (role.remote)
{
userProps->second.remote = role.remote;
}
if (role.userGroups)
{
userProps->second.userGroups = role.userGroups;
}
if (role.passwordExpired)
{
userProps->second.passwordExpired = role.passwordExpired;
}
}
void onGetManagedObjects(
const boost::system::error_code& ec,
const dbus::utility::ManagedObjectType& managedObjects)
{
if (ec)
{
BMCWEB_LOG_DEBUG("User manager call failed, ignoring");
return;
}
for (const auto& managedObj : managedObjects)
{
std::string name =
sdbusplus::message::object_path(managedObj.first).filename();
if (name.empty())
{
continue;
}
UserFields role = extractUserRole(managedObj.second);
roleMap.emplace(name, role);
}
}
static constexpr const char* userObjPath = "/xyz/openbmc_project/user";
UserRoleMap() :
userAddedSignal(
*crow::connections::systemBus,
sdbusplus::bus::match::rules::interfacesAdded(userObjPath),
std::bind_front(&UserRoleMap::userAdded, this)),
userRemovedSignal(
*crow::connections::systemBus,
sdbusplus::bus::match::rules::interfacesRemoved(userObjPath),
std::bind_front(&UserRoleMap::userRemoved, this)),
userPropertiesChangedSignal(
*crow::connections::systemBus,
sdbusplus::bus::match::rules::propertiesChangedNamespace(
userObjPath, "xyz.openbmc_project.User.Attributes"),
std::bind_front(&UserRoleMap::userPropertiesChanged, this))
{
dbus::utility::getManagedObjects(
"xyz.openbmc_project.User.Manager", {userObjPath},
std::bind_front(&UserRoleMap::onGetManagedObjects, this));
}
// Map of username -> role
boost::container::flat_map<std::string, UserFields, std::less<>> roleMap;
// These MUST be last, otherwise destruction can cause race conditions.
sdbusplus::bus::match_t userAddedSignal;
sdbusplus::bus::match_t userRemovedSignal;
sdbusplus::bus::match_t userPropertiesChangedSignal;
};
} // namespace crow