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