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