blob: 7fb545a9c1befbb64fd2f56b20708e75a76d62ae [file] [log] [blame]
Ed Tanous08bbe112023-04-06 13:10:02 -07001#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
17namespace crow
18{
Gunnar Mills59ba6382023-08-01 12:41:17 -050019// Populate session with user information.
20inline 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 {
Jonathan Doman5ab47852023-08-17 18:19:19 -070086 req.session->userGroups = *userGroups;
87 }
88 else
89 {
90 req.session->userGroups.clear();
Gunnar Mills59ba6382023-08-01 12:41:17 -050091 }
92
93 return true;
94}
Ed Tanous08bbe112023-04-06 13:10:02 -070095
96inline bool
97 isUserPrivileged(Request& req,
98 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
99 BaseRule& rule)
100{
Ed Tanousd3dfb6e2023-07-27 09:33:49 -0700101 if (req.session == nullptr)
102 {
103 return false;
104 }
Ed Tanous08bbe112023-04-06 13:10:02 -0700105 // 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"});
Ed Tanous62598e32023-07-17 17:06:25 -0700115 BMCWEB_LOG_DEBUG("Operation limited to ConfigureSelf");
Ed Tanous08bbe112023-04-06 13:10:02 -0700116 }
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
Gunnar Mills59ba6382023-08-01 12:41:17 -0500131 req.userRole = req.session->userRole;
Ed Tanous08bbe112023-04-06 13:10:02 -0700132 return true;
133}
134
135template <typename CallbackFn>
Gunnar Mills59ba6382023-08-01 12:41:17 -0500136void 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
168template <typename CallbackFn>
Ed Tanous08bbe112023-04-06 13:10:02 -0700169void 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;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500178 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);
Ed Tanous08bbe112023-04-06 13:10:02 -0700187}
188
189} // namespace crow