blob: 355f8770af6117911782d4a5423b94b276d1e349 [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
Patrick Williamsbd79bce2024-08-16 15:22:20 -040066inline bool isUserPrivileged(
67 Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
68 BaseRule& rule)
Ed Tanous08bbe112023-04-06 13:10:02 -070069{
Ed Tanousd3dfb6e2023-07-27 09:33:49 -070070 if (req.session == nullptr)
71 {
72 return false;
73 }
Ed Tanous08bbe112023-04-06 13:10:02 -070074 // 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"});
Ed Tanous62598e32023-07-17 17:06:25 -070084 BMCWEB_LOG_DEBUG("Operation limited to ConfigureSelf");
Ed Tanous08bbe112023-04-06 13:10:02 -070085 }
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
Gunnar Mills59ba6382023-08-01 12:41:17 -0500100 req.userRole = req.session->userRole;
Ed Tanous08bbe112023-04-06 13:10:02 -0700101 return true;
102}
103
Paul Fertser478c5a52024-06-26 22:27:59 +0000104inline bool afterGetUserInfoValidate(
105 Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
106 BaseRule& rule, const dbus::utility::DBusPropertiesMap& userInfoMap)
Gunnar Mills59ba6382023-08-01 12:41:17 -0500107{
Paul Fertser478c5a52024-06-26 22:27:59 +0000108 if (req.session == nullptr || !populateUserInfo(*req.session, userInfoMap))
Gunnar Mills59ba6382023-08-01 12:41:17 -0500109 {
110 BMCWEB_LOG_ERROR("Failed to populate user information");
111 asyncResp->res.result(
112 boost::beast::http::status::internal_server_error);
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700113 return false;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500114 }
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);
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700121 return false;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500122 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700123
124 return true;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500125}
126
127template <typename CallbackFn>
Paul Fertser478c5a52024-06-26 22:27:59 +0000128void 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 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400136 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 },
Paul Fertser478c5a52024-06-26 22:27:59 +0000145 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
146 "xyz.openbmc_project.User.Manager", "GetUserInfo", username);
147}
148
149template <typename CallbackFn>
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700150void validatePrivilege(const std::shared_ptr<Request>& req,
Ed Tanous08bbe112023-04-06 13:10:02 -0700151 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
152 BaseRule& rule, CallbackFn&& callback)
153{
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700154 if (req->session == nullptr)
Ed Tanous08bbe112023-04-06 13:10:02 -0700155 {
156 return;
157 }
Paul Fertser478c5a52024-06-26 22:27:59 +0000158
159 requestUserInfo(
160 req->session->username, asyncResp,
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700161 [req, asyncResp, &rule, callback = std::forward<CallbackFn>(callback)](
Gunnar Mills59ba6382023-08-01 12:41:17 -0500162 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400163 if (afterGetUserInfoValidate(*req, asyncResp, rule, userInfoMap))
164 {
165 callback();
166 }
167 });
Paul Fertser478c5a52024-06-26 22:27:59 +0000168}
169
170template <typename CallbackFn>
171void 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) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400180 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 });
Ed Tanous08bbe112023-04-06 13:10:02 -0700189}
190
191} // namespace crow