| #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 |