blob: 377a41cbeea378740d52528df68ebf084b587b77 [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{
19// 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 (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
91inline 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
126template <typename CallbackFn>
127void 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
159template <typename CallbackFn>
160void 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