blob: 02d134df63dd9902d818e273c547f6240302cd5c [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>
Paul Fertser478c5a52024-06-26 22:27:59 +000015#include <string>
16#include <utility>
Ed Tanous08bbe112023-04-06 13:10:02 -070017#include <vector>
18
19namespace crow
20{
Gunnar Mills59ba6382023-08-01 12:41:17 -050021// Populate session with user information.
22inline bool
Paul Fertser478c5a52024-06-26 22:27:59 +000023 populateUserInfo(persistent_data::UserSession& session,
Gunnar Mills59ba6382023-08-01 12:41:17 -050024 const dbus::utility::DBusPropertiesMap& userInfoMap)
25{
Jonathan Doman522377d2023-08-18 15:27:54 -070026 std::string userRole;
27 bool remoteUser = false;
28 std::optional<bool> passwordExpired;
29 std::optional<std::vector<std::string>> userGroups;
Gunnar Mills59ba6382023-08-01 12:41:17 -050030
Jonathan Doman522377d2023-08-18 15:27:54 -070031 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)
Gunnar Mills59ba6382023-08-01 12:41:17 -050037 {
Jonathan Doman522377d2023-08-18 15:27:54 -070038 BMCWEB_LOG_ERROR("Failed to unpack user properties.");
Gunnar Mills59ba6382023-08-01 12:41:17 -050039 return false;
40 }
Jonathan Doman522377d2023-08-18 15:27:54 -070041
42 if (!remoteUser && (!passwordExpired || !userGroups))
Gunnar Mills59ba6382023-08-01 12:41:17 -050043 {
Jonathan Doman522377d2023-08-18 15:27:54 -070044 BMCWEB_LOG_ERROR(
45 "Missing UserPasswordExpired or UserGroups property for local user");
46 return false;
Gunnar Mills59ba6382023-08-01 12:41:17 -050047 }
Jonathan Doman522377d2023-08-18 15:27:54 -070048
Paul Fertser478c5a52024-06-26 22:27:59 +000049 session.userRole = userRole;
50 BMCWEB_LOG_DEBUG("userName = {} userRole = {}", session.username, userRole);
Gunnar Mills59ba6382023-08-01 12:41:17 -050051
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.
Paul Fertser478c5a52024-06-26 22:27:59 +000055 session.isConfigureSelfOnly = passwordExpired.value_or(false);
Gunnar Mills59ba6382023-08-01 12:41:17 -050056
Paul Fertser478c5a52024-06-26 22:27:59 +000057 session.userGroups.clear();
Jonathan Doman522377d2023-08-18 15:27:54 -070058 if (userGroups)
Gunnar Mills59ba6382023-08-01 12:41:17 -050059 {
Paul Fertser478c5a52024-06-26 22:27:59 +000060 session.userGroups.swap(*userGroups);
Gunnar Mills59ba6382023-08-01 12:41:17 -050061 }
62
63 return true;
64}
Ed Tanous08bbe112023-04-06 13:10:02 -070065
66inline bool
67 isUserPrivileged(Request& req,
68 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
69 BaseRule& rule)
70{
Ed Tanousd3dfb6e2023-07-27 09:33:49 -070071 if (req.session == nullptr)
72 {
73 return false;
74 }
Ed Tanous08bbe112023-04-06 13:10:02 -070075 // Get the user's privileges from the role
76 redfish::Privileges userPrivileges =
77 redfish::getUserPrivileges(*req.session);
78
79 // Modify privileges if isConfigureSelfOnly.
80 if (req.session->isConfigureSelfOnly)
81 {
82 // Remove all privileges except ConfigureSelf
83 userPrivileges =
84 userPrivileges.intersection(redfish::Privileges{"ConfigureSelf"});
Ed Tanous62598e32023-07-17 17:06:25 -070085 BMCWEB_LOG_DEBUG("Operation limited to ConfigureSelf");
Ed Tanous08bbe112023-04-06 13:10:02 -070086 }
87
88 if (!rule.checkPrivileges(userPrivileges))
89 {
90 asyncResp->res.result(boost::beast::http::status::forbidden);
91 if (req.session->isConfigureSelfOnly)
92 {
93 redfish::messages::passwordChangeRequired(
94 asyncResp->res,
95 boost::urls::format("/redfish/v1/AccountService/Accounts/{}",
96 req.session->username));
97 }
98 return false;
99 }
100
Gunnar Mills59ba6382023-08-01 12:41:17 -0500101 req.userRole = req.session->userRole;
Ed Tanous08bbe112023-04-06 13:10:02 -0700102 return true;
103}
104
Paul Fertser478c5a52024-06-26 22:27:59 +0000105inline bool afterGetUserInfoValidate(
106 Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
107 BaseRule& rule, const dbus::utility::DBusPropertiesMap& userInfoMap)
Gunnar Mills59ba6382023-08-01 12:41:17 -0500108{
Paul Fertser478c5a52024-06-26 22:27:59 +0000109 if (req.session == nullptr || !populateUserInfo(*req.session, userInfoMap))
Gunnar Mills59ba6382023-08-01 12:41:17 -0500110 {
111 BMCWEB_LOG_ERROR("Failed to populate user information");
112 asyncResp->res.result(
113 boost::beast::http::status::internal_server_error);
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700114 return false;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500115 }
116
117 if (!isUserPrivileged(req, asyncResp, rule))
118 {
119 // User is not privileged
120 BMCWEB_LOG_ERROR("Insufficient Privilege");
121 asyncResp->res.result(boost::beast::http::status::forbidden);
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700122 return false;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500123 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700124
125 return true;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500126}
127
128template <typename CallbackFn>
Paul Fertser478c5a52024-06-26 22:27:59 +0000129void requestUserInfo(const std::string& username,
130 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
131 CallbackFn&& callback)
132{
133 crow::connections::systemBus->async_method_call(
134 [asyncResp, callback = std::forward<CallbackFn>(callback)](
135 const boost::system::error_code& ec,
136 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
137 if (ec)
138 {
139 BMCWEB_LOG_ERROR("GetUserInfo failed...");
140 asyncResp->res.result(
141 boost::beast::http::status::internal_server_error);
142 return;
143 }
144 callback(userInfoMap);
145 },
146 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
147 "xyz.openbmc_project.User.Manager", "GetUserInfo", username);
148}
149
150template <typename CallbackFn>
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700151void validatePrivilege(const std::shared_ptr<Request>& req,
Ed Tanous08bbe112023-04-06 13:10:02 -0700152 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
153 BaseRule& rule, CallbackFn&& callback)
154{
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700155 if (req->session == nullptr)
Ed Tanous08bbe112023-04-06 13:10:02 -0700156 {
157 return;
158 }
Paul Fertser478c5a52024-06-26 22:27:59 +0000159
160 requestUserInfo(
161 req->session->username, asyncResp,
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700162 [req, asyncResp, &rule, callback = std::forward<CallbackFn>(callback)](
Gunnar Mills59ba6382023-08-01 12:41:17 -0500163 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
Paul Fertser478c5a52024-06-26 22:27:59 +0000164 if (afterGetUserInfoValidate(*req, asyncResp, rule, userInfoMap))
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700165 {
166 callback();
167 }
Paul Fertser478c5a52024-06-26 22:27:59 +0000168 });
169}
170
171template <typename CallbackFn>
172void getUserInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
173 const std::string& username,
174 std::shared_ptr<persistent_data::UserSession>& session,
175 CallbackFn&& callback)
176{
177 requestUserInfo(
178 username, asyncResp,
179 [asyncResp, session, callback = std::forward<CallbackFn>(callback)](
180 const dbus::utility::DBusPropertiesMap& userInfoMap) {
181 if (!populateUserInfo(*session, userInfoMap))
182 {
183 BMCWEB_LOG_ERROR("Failed to populate user information");
184 asyncResp->res.result(
185 boost::beast::http::status::internal_server_error);
186 return;
187 }
188 callback();
189 });
Ed Tanous08bbe112023-04-06 13:10:02 -0700190}
191
192} // namespace crow