1 #pragma once 2 3 #include "dbus_utility.hpp" 4 #include "error_messages.hpp" 5 #include "http_request.hpp" 6 #include "http_response.hpp" 7 #include "logging.hpp" 8 #include "routing/baserule.hpp" 9 #include "utils/dbus_utils.hpp" 10 11 #include <boost/url/format.hpp> 12 #include <sdbusplus/unpack_properties.hpp> 13 14 #include <memory> 15 #include <vector> 16 17 namespace crow 18 { 19 // Populate session with user information. 20 inline bool 21 populateUserInfo(Request& req, 22 const dbus::utility::DBusPropertiesMap& userInfoMap) 23 { 24 if (req.session == nullptr) 25 { 26 return false; 27 } 28 29 std::string userRole; 30 bool remoteUser = false; 31 std::optional<bool> passwordExpired; 32 std::optional<std::vector<std::string>> userGroups; 33 34 const bool success = sdbusplus::unpackPropertiesNoThrow( 35 redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap, "UserPrivilege", 36 userRole, "RemoteUser", remoteUser, "UserPasswordExpired", 37 passwordExpired, "UserGroups", userGroups); 38 39 if (!success) 40 { 41 BMCWEB_LOG_ERROR("Failed to unpack user properties."); 42 return false; 43 } 44 45 if (!remoteUser && (!passwordExpired || !userGroups)) 46 { 47 BMCWEB_LOG_ERROR( 48 "Missing UserPasswordExpired or UserGroups property for local user"); 49 return false; 50 } 51 52 req.session->userRole = userRole; 53 BMCWEB_LOG_DEBUG("userName = {} userRole = {}", req.session->username, 54 userRole); 55 56 // Set isConfigureSelfOnly based on D-Bus results. This 57 // ignores the results from both pamAuthenticateUser and the 58 // value from any previous use of this session. 59 req.session->isConfigureSelfOnly = passwordExpired.value_or(false); 60 61 req.session->userGroups.clear(); 62 if (userGroups) 63 { 64 req.session->userGroups.swap(*userGroups); 65 } 66 67 return true; 68 } 69 70 inline bool 71 isUserPrivileged(Request& req, 72 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 73 BaseRule& rule) 74 { 75 if (req.session == nullptr) 76 { 77 return false; 78 } 79 // Get the user's privileges from the role 80 redfish::Privileges userPrivileges = 81 redfish::getUserPrivileges(*req.session); 82 83 // Modify privileges if isConfigureSelfOnly. 84 if (req.session->isConfigureSelfOnly) 85 { 86 // Remove all privileges except ConfigureSelf 87 userPrivileges = 88 userPrivileges.intersection(redfish::Privileges{"ConfigureSelf"}); 89 BMCWEB_LOG_DEBUG("Operation limited to ConfigureSelf"); 90 } 91 92 if (!rule.checkPrivileges(userPrivileges)) 93 { 94 asyncResp->res.result(boost::beast::http::status::forbidden); 95 if (req.session->isConfigureSelfOnly) 96 { 97 redfish::messages::passwordChangeRequired( 98 asyncResp->res, 99 boost::urls::format("/redfish/v1/AccountService/Accounts/{}", 100 req.session->username)); 101 } 102 return false; 103 } 104 105 req.userRole = req.session->userRole; 106 return true; 107 } 108 109 template <typename CallbackFn> 110 void afterGetUserInfo(Request& req, 111 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 112 BaseRule& rule, CallbackFn&& callback, 113 const boost::system::error_code& ec, 114 const dbus::utility::DBusPropertiesMap& userInfoMap) 115 { 116 if (ec) 117 { 118 BMCWEB_LOG_ERROR("GetUserInfo failed..."); 119 asyncResp->res.result( 120 boost::beast::http::status::internal_server_error); 121 return; 122 } 123 124 if (!populateUserInfo(req, userInfoMap)) 125 { 126 BMCWEB_LOG_ERROR("Failed to populate user information"); 127 asyncResp->res.result( 128 boost::beast::http::status::internal_server_error); 129 return; 130 } 131 132 if (!isUserPrivileged(req, asyncResp, rule)) 133 { 134 // User is not privileged 135 BMCWEB_LOG_ERROR("Insufficient Privilege"); 136 asyncResp->res.result(boost::beast::http::status::forbidden); 137 return; 138 } 139 callback(req); 140 } 141 142 template <typename CallbackFn> 143 void validatePrivilege(Request& req, 144 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 145 BaseRule& rule, CallbackFn&& callback) 146 { 147 if (req.session == nullptr) 148 { 149 return; 150 } 151 std::string username = req.session->username; 152 crow::connections::systemBus->async_method_call( 153 [&req, asyncResp, &rule, callback(std::forward<CallbackFn>(callback))]( 154 const boost::system::error_code& ec, 155 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable { 156 afterGetUserInfo(req, asyncResp, rule, 157 std::forward<CallbackFn>(callback), ec, userInfoMap); 158 }, 159 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 160 "xyz.openbmc_project.User.Manager", "GetUserInfo", username); 161 } 162 163 } // namespace crow 164