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 std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 23 const dbus::utility::DBusPropertiesMap& userInfoMap) 24 { 25 const std::string* userRolePtr = nullptr; 26 const bool* remoteUser = nullptr; 27 const bool* passwordExpired = nullptr; 28 const std::vector<std::string>* userGroups = nullptr; 29 30 const bool success = sdbusplus::unpackPropertiesNoThrow( 31 redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap, "UserPrivilege", 32 userRolePtr, "RemoteUser", remoteUser, "UserPasswordExpired", 33 passwordExpired, "UserGroups", userGroups); 34 35 if (!success) 36 { 37 BMCWEB_LOG_ERROR("Failed to unpack user properties."); 38 asyncResp->res.result( 39 boost::beast::http::status::internal_server_error); 40 return false; 41 } 42 43 if (req.session == nullptr) 44 { 45 return false; 46 } 47 48 if (userRolePtr != nullptr) 49 { 50 req.session->userRole = *userRolePtr; 51 BMCWEB_LOG_DEBUG("userName = {} userRole = {}", req.session->username, 52 *userRolePtr); 53 } 54 55 if (remoteUser == nullptr) 56 { 57 BMCWEB_LOG_ERROR("RemoteUser property missing or wrong type"); 58 asyncResp->res.result( 59 boost::beast::http::status::internal_server_error); 60 return false; 61 } 62 bool expired = false; 63 if (passwordExpired == nullptr) 64 { 65 if (!*remoteUser) 66 { 67 BMCWEB_LOG_ERROR("UserPasswordExpired property is expected for" 68 " local user but is missing or wrong type"); 69 asyncResp->res.result( 70 boost::beast::http::status::internal_server_error); 71 return false; 72 } 73 } 74 else 75 { 76 expired = *passwordExpired; 77 } 78 79 // Set isConfigureSelfOnly based on D-Bus results. This 80 // ignores the results from both pamAuthenticateUser and the 81 // value from any previous use of this session. 82 req.session->isConfigureSelfOnly = expired; 83 84 if (userGroups != nullptr) 85 { 86 req.session->userGroups = *userGroups; 87 } 88 else 89 { 90 req.session->userGroups.clear(); 91 } 92 93 return true; 94 } 95 96 inline bool 97 isUserPrivileged(Request& req, 98 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 99 BaseRule& rule) 100 { 101 if (req.session == nullptr) 102 { 103 return false; 104 } 105 // Get the user's privileges from the role 106 redfish::Privileges userPrivileges = 107 redfish::getUserPrivileges(*req.session); 108 109 // Modify privileges if isConfigureSelfOnly. 110 if (req.session->isConfigureSelfOnly) 111 { 112 // Remove all privileges except ConfigureSelf 113 userPrivileges = 114 userPrivileges.intersection(redfish::Privileges{"ConfigureSelf"}); 115 BMCWEB_LOG_DEBUG("Operation limited to ConfigureSelf"); 116 } 117 118 if (!rule.checkPrivileges(userPrivileges)) 119 { 120 asyncResp->res.result(boost::beast::http::status::forbidden); 121 if (req.session->isConfigureSelfOnly) 122 { 123 redfish::messages::passwordChangeRequired( 124 asyncResp->res, 125 boost::urls::format("/redfish/v1/AccountService/Accounts/{}", 126 req.session->username)); 127 } 128 return false; 129 } 130 131 req.userRole = req.session->userRole; 132 return true; 133 } 134 135 template <typename CallbackFn> 136 void afterGetUserInfo(Request& req, 137 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 138 BaseRule& rule, CallbackFn&& callback, 139 const boost::system::error_code& ec, 140 const dbus::utility::DBusPropertiesMap& userInfoMap) 141 { 142 if (ec) 143 { 144 BMCWEB_LOG_ERROR("GetUserInfo failed..."); 145 asyncResp->res.result( 146 boost::beast::http::status::internal_server_error); 147 return; 148 } 149 150 if (!populateUserInfo(req, asyncResp, userInfoMap)) 151 { 152 BMCWEB_LOG_ERROR("Failed to populate user information"); 153 asyncResp->res.result( 154 boost::beast::http::status::internal_server_error); 155 return; 156 } 157 158 if (!isUserPrivileged(req, asyncResp, rule)) 159 { 160 // User is not privileged 161 BMCWEB_LOG_ERROR("Insufficient Privilege"); 162 asyncResp->res.result(boost::beast::http::status::forbidden); 163 return; 164 } 165 callback(req); 166 } 167 168 template <typename CallbackFn> 169 void validatePrivilege(Request& req, 170 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 171 BaseRule& rule, CallbackFn&& callback) 172 { 173 if (req.session == nullptr) 174 { 175 return; 176 } 177 std::string username = req.session->username; 178 crow::connections::systemBus->async_method_call( 179 [&req, asyncResp, &rule, callback(std::forward<CallbackFn>(callback))]( 180 const boost::system::error_code& ec, 181 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable { 182 afterGetUserInfo(req, asyncResp, rule, 183 std::forward<CallbackFn>(callback), ec, userInfoMap); 184 }, 185 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", 186 "xyz.openbmc_project.User.Manager", "GetUserInfo", username); 187 } 188 189 } // namespace crow 190