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