blob: 355f8770af6117911782d4a5423b94b276d1e349 [file] [log] [blame]
#pragma once
#include "dbus_utility.hpp"
#include "error_messages.hpp"
#include "http_request.hpp"
#include "http_response.hpp"
#include "logging.hpp"
#include "routing/baserule.hpp"
#include "utils/dbus_utils.hpp"
#include <boost/url/format.hpp>
#include <sdbusplus/unpack_properties.hpp>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace crow
{
// Populate session with user information.
inline bool
populateUserInfo(persistent_data::UserSession& session,
const dbus::utility::DBusPropertiesMap& userInfoMap)
{
std::string userRole;
bool remoteUser = false;
std::optional<bool> passwordExpired;
std::optional<std::vector<std::string>> userGroups;
const bool success = sdbusplus::unpackPropertiesNoThrow(
redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap, "UserPrivilege",
userRole, "RemoteUser", remoteUser, "UserPasswordExpired",
passwordExpired, "UserGroups", userGroups);
if (!success)
{
BMCWEB_LOG_ERROR("Failed to unpack user properties.");
return false;
}
if (!remoteUser && (!passwordExpired || !userGroups))
{
BMCWEB_LOG_ERROR(
"Missing UserPasswordExpired or UserGroups property for local user");
return false;
}
session.userRole = userRole;
BMCWEB_LOG_DEBUG("userName = {} userRole = {}", session.username, userRole);
// Set isConfigureSelfOnly based on D-Bus results. This
// ignores the results from both pamAuthenticateUser and the
// value from any previous use of this session.
session.isConfigureSelfOnly = passwordExpired.value_or(false);
session.userGroups.clear();
if (userGroups)
{
session.userGroups.swap(*userGroups);
}
return true;
}
inline bool isUserPrivileged(
Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
BaseRule& rule)
{
if (req.session == nullptr)
{
return false;
}
// Get the user's privileges from the role
redfish::Privileges userPrivileges =
redfish::getUserPrivileges(*req.session);
// Modify privileges if isConfigureSelfOnly.
if (req.session->isConfigureSelfOnly)
{
// Remove all privileges except ConfigureSelf
userPrivileges =
userPrivileges.intersection(redfish::Privileges{"ConfigureSelf"});
BMCWEB_LOG_DEBUG("Operation limited to ConfigureSelf");
}
if (!rule.checkPrivileges(userPrivileges))
{
asyncResp->res.result(boost::beast::http::status::forbidden);
if (req.session->isConfigureSelfOnly)
{
redfish::messages::passwordChangeRequired(
asyncResp->res,
boost::urls::format("/redfish/v1/AccountService/Accounts/{}",
req.session->username));
}
return false;
}
req.userRole = req.session->userRole;
return true;
}
inline bool afterGetUserInfoValidate(
Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
BaseRule& rule, const dbus::utility::DBusPropertiesMap& userInfoMap)
{
if (req.session == nullptr || !populateUserInfo(*req.session, userInfoMap))
{
BMCWEB_LOG_ERROR("Failed to populate user information");
asyncResp->res.result(
boost::beast::http::status::internal_server_error);
return false;
}
if (!isUserPrivileged(req, asyncResp, rule))
{
// User is not privileged
BMCWEB_LOG_ERROR("Insufficient Privilege");
asyncResp->res.result(boost::beast::http::status::forbidden);
return false;
}
return true;
}
template <typename CallbackFn>
void requestUserInfo(const std::string& username,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
CallbackFn&& callback)
{
crow::connections::systemBus->async_method_call(
[asyncResp, callback = std::forward<CallbackFn>(callback)](
const boost::system::error_code& ec,
const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
if (ec)
{
BMCWEB_LOG_ERROR("GetUserInfo failed...");
asyncResp->res.result(
boost::beast::http::status::internal_server_error);
return;
}
callback(userInfoMap);
},
"xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
"xyz.openbmc_project.User.Manager", "GetUserInfo", username);
}
template <typename CallbackFn>
void validatePrivilege(const std::shared_ptr<Request>& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
BaseRule& rule, CallbackFn&& callback)
{
if (req->session == nullptr)
{
return;
}
requestUserInfo(
req->session->username, asyncResp,
[req, asyncResp, &rule, callback = std::forward<CallbackFn>(callback)](
const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
if (afterGetUserInfoValidate(*req, asyncResp, rule, userInfoMap))
{
callback();
}
});
}
template <typename CallbackFn>
void getUserInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& username,
std::shared_ptr<persistent_data::UserSession>& session,
CallbackFn&& callback)
{
requestUserInfo(
username, asyncResp,
[asyncResp, session, callback = std::forward<CallbackFn>(callback)](
const dbus::utility::DBusPropertiesMap& userInfoMap) {
if (!populateUserInfo(*session, userInfoMap))
{
BMCWEB_LOG_ERROR("Failed to populate user information");
asyncResp->res.result(
boost::beast::http::status::internal_server_error);
return;
}
callback();
});
}
} // namespace crow