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