diff options
-rw-r--r-- | http/http_request.hpp | 1 | ||||
-rw-r--r-- | include/dbus_privileges.hpp | 134 | ||||
-rw-r--r-- | include/sessions.hpp | 8 | ||||
-rw-r--r-- | include/user_role_map.hpp | 278 | ||||
-rw-r--r-- | src/webserver_main.cpp | 4 |
5 files changed, 116 insertions, 309 deletions
diff --git a/http/http_request.hpp b/http/http_request.hpp index 4762a9bb26..5ce434b921 100644 --- a/http/http_request.hpp +++ b/http/http_request.hpp @@ -32,6 +32,7 @@ struct Request std::shared_ptr<persistent_data::UserSession> session; + std::string userRole{}; Request(boost::beast::http::request<boost::beast::http::string_body> reqIn, std::error_code& ec) : req(std::move(reqIn)) diff --git a/include/dbus_privileges.hpp b/include/dbus_privileges.hpp index 07a1216cdc..fca4a137d3 100644 --- a/include/dbus_privileges.hpp +++ b/include/dbus_privileges.hpp @@ -6,11 +6,9 @@ #include "http_response.hpp" #include "logging.hpp" #include "routing/baserule.hpp" -#include "user_role_map.hpp" #include "utils/dbus_utils.hpp" #include <boost/url/format.hpp> -#include <sdbusplus/bus/match.hpp> #include <sdbusplus/unpack_properties.hpp> #include <memory> @@ -18,6 +16,82 @@ namespace crow { +// Populate session with user information. +inline bool + populateUserInfo(Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const dbus::utility::DBusPropertiesMap& userInfoMap) +{ + const std::string* userRolePtr = nullptr; + const bool* remoteUser = nullptr; + const bool* passwordExpired = nullptr; + const std::vector<std::string>* userGroups = nullptr; + + const bool success = sdbusplus::unpackPropertiesNoThrow( + redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap, "UserPrivilege", + userRolePtr, "RemoteUser", remoteUser, "UserPasswordExpired", + passwordExpired, "UserGroups", userGroups); + + if (!success) + { + BMCWEB_LOG_ERROR("Failed to unpack user properties."); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return false; + } + + if (req.session == nullptr) + { + return false; + } + + if (userRolePtr != nullptr) + { + req.session->userRole = *userRolePtr; + BMCWEB_LOG_DEBUG("userName = {} userRole = {}", req.session->username, + *userRolePtr); + } + + if (remoteUser == nullptr) + { + BMCWEB_LOG_ERROR("RemoteUser property missing or wrong type"); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return false; + } + bool expired = false; + if (passwordExpired == nullptr) + { + if (!*remoteUser) + { + BMCWEB_LOG_ERROR("UserPasswordExpired property is expected for" + " local user but is missing or wrong type"); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return false; + } + } + else + { + expired = *passwordExpired; + } + + // Set isConfigureSelfOnly based on D-Bus results. This + // ignores the results from both pamAuthenticateUser and the + // value from any previous use of this session. + req.session->isConfigureSelfOnly = expired; + + if (userGroups != nullptr) + { + // Populate session with user groups. + for (const auto& userGroup : *userGroups) + { + req.session->userGroups.emplace_back(userGroup); + } + } + + return true; +} inline bool isUserPrivileged(Request& req, @@ -54,42 +128,62 @@ inline bool return false; } + req.userRole = req.session->userRole; return true; } template <typename CallbackFn> -void validatePrivilege(Request& req, - const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - BaseRule& rule, CallbackFn&& callback) +void afterGetUserInfo(Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + BaseRule& rule, CallbackFn&& callback, + const boost::system::error_code& ec, + const dbus::utility::DBusPropertiesMap& userInfoMap) { - if (req.session == nullptr) + if (ec) { + BMCWEB_LOG_ERROR("GetUserInfo failed..."); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); return; } - std::string username = req.session->username; - UserFields props = - UserRoleMap::getInstance().getUserRole(req.session->username); - if (props.userRole) - { - req.session->userRole = props.userRole.value_or(""); - } - if (props.passwordExpired) - { - req.session->isConfigureSelfOnly = *props.passwordExpired; - } - if (props.userGroups) + + if (!populateUserInfo(req, asyncResp, userInfoMap)) { - req.session->userGroups = std::move(*props.userGroups); + BMCWEB_LOG_ERROR("Failed to populate user information"); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; } if (!isUserPrivileged(req, asyncResp, rule)) { // User is not privileged - BMCWEB_LOG_WARNING("Insufficient Privilege"); + BMCWEB_LOG_ERROR("Insufficient Privilege"); asyncResp->res.result(boost::beast::http::status::forbidden); return; } callback(req); } +template <typename CallbackFn> +void validatePrivilege(Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + BaseRule& rule, CallbackFn&& callback) +{ + if (req.session == nullptr) + { + return; + } + std::string username = req.session->username; + crow::connections::systemBus->async_method_call( + [&req, asyncResp, &rule, callback(std::forward<CallbackFn>(callback))]( + const boost::system::error_code& ec, + const dbus::utility::DBusPropertiesMap& userInfoMap) mutable { + afterGetUserInfo(req, asyncResp, rule, + std::forward<CallbackFn>(callback), ec, userInfoMap); + }, + "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", + "xyz.openbmc_project.User.Manager", "GetUserInfo", username); +} + } // namespace crow diff --git a/include/sessions.hpp b/include/sessions.hpp index 90a1de93de..cb7f78e78a 100644 --- a/include/sessions.hpp +++ b/include/sessions.hpp @@ -2,12 +2,10 @@ #include "logging.hpp" #include "ossl_random.hpp" -#include "user_role_map.hpp" #include "utility.hpp" #include "utils/ip_utils.hpp" #include <nlohmann/json.hpp> -#include <sdbusplus/message.hpp> #include <algorithm> #include <csignal> @@ -258,15 +256,11 @@ class SessionStore } } - std::string userRole = crow::UserRoleMap::getInstance() - .getUserRole(username) - .userRole.value_or(""); - auto session = std::make_shared<UserSession>(UserSession{ uniqueId, sessionToken, std::string(username), csrfToken, clientId, redfish::ip_util::toString(clientIp), std::chrono::steady_clock::now(), persistence, false, - isConfigureSelfOnly, userRole}); + isConfigureSelfOnly}); auto it = authTokens.emplace(sessionToken, session); // Only need to write to disk if session isn't about to be destroyed. needWrite = persistence == PersistenceType::TIMEOUT; diff --git a/include/user_role_map.hpp b/include/user_role_map.hpp deleted file mode 100644 index b574c4e7af..0000000000 --- a/include/user_role_map.hpp +++ /dev/null @@ -1,278 +0,0 @@ -#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 diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp index 34fe18f07d..67e2aaef04 100644 --- a/src/webserver_main.cpp +++ b/src/webserver_main.cpp @@ -18,7 +18,6 @@ #include "security_headers.hpp" #include "ssl_key_handler.hpp" #include "user_monitor.hpp" -#include "user_role_map.hpp" #include "vm_websocket.hpp" #include "webassets.hpp" @@ -144,9 +143,6 @@ static int run() crow::hostname_monitor::registerHostnameSignal(); #endif - // Init the user role map - crow::UserRoleMap::getInstance(); - bmcweb::registerUserRemovedSignal(); app.run(); |