blob: 9b122448f32d65478038c63b26c13bd3fb1705af [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanous08bbe112023-04-06 13:10:02 -07003#pragma once
4
Ed Tanousd7857202025-01-28 15:32:26 -08005#include "async_resp.hpp"
6#include "dbus_singleton.hpp"
Ed Tanous08bbe112023-04-06 13:10:02 -07007#include "dbus_utility.hpp"
8#include "error_messages.hpp"
9#include "http_request.hpp"
10#include "http_response.hpp"
11#include "logging.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080012#include "privileges.hpp"
Ed Tanous08bbe112023-04-06 13:10:02 -070013#include "routing/baserule.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080014#include "sessions.hpp"
Ed Tanous08bbe112023-04-06 13:10:02 -070015#include "utils/dbus_utils.hpp"
16
Ed Tanousd7857202025-01-28 15:32:26 -080017#include <boost/beast/http/status.hpp>
Ed Tanous08bbe112023-04-06 13:10:02 -070018#include <boost/url/format.hpp>
19#include <sdbusplus/unpack_properties.hpp>
20
Ed Tanousbb1c7d32025-02-09 09:39:19 -080021#include <functional>
Ed Tanous08bbe112023-04-06 13:10:02 -070022#include <memory>
Ed Tanousd7857202025-01-28 15:32:26 -080023#include <optional>
Paul Fertser478c5a52024-06-26 22:27:59 +000024#include <string>
25#include <utility>
Ed Tanous08bbe112023-04-06 13:10:02 -070026#include <vector>
27
28namespace crow
29{
Gunnar Mills59ba6382023-08-01 12:41:17 -050030// Populate session with user information.
Patrick Williams504af5a2025-02-03 14:29:03 -050031inline bool populateUserInfo(
32 persistent_data::UserSession& session,
33 const dbus::utility::DBusPropertiesMap& userInfoMap)
Gunnar Mills59ba6382023-08-01 12:41:17 -050034{
Jonathan Doman522377d2023-08-18 15:27:54 -070035 std::string userRole;
36 bool remoteUser = false;
37 std::optional<bool> passwordExpired;
38 std::optional<std::vector<std::string>> userGroups;
Gunnar Mills59ba6382023-08-01 12:41:17 -050039
Jonathan Doman522377d2023-08-18 15:27:54 -070040 const bool success = sdbusplus::unpackPropertiesNoThrow(
41 redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap, "UserPrivilege",
42 userRole, "RemoteUser", remoteUser, "UserPasswordExpired",
43 passwordExpired, "UserGroups", userGroups);
44
45 if (!success)
Gunnar Mills59ba6382023-08-01 12:41:17 -050046 {
Jonathan Doman522377d2023-08-18 15:27:54 -070047 BMCWEB_LOG_ERROR("Failed to unpack user properties.");
Gunnar Mills59ba6382023-08-01 12:41:17 -050048 return false;
49 }
Jonathan Doman522377d2023-08-18 15:27:54 -070050
51 if (!remoteUser && (!passwordExpired || !userGroups))
Gunnar Mills59ba6382023-08-01 12:41:17 -050052 {
Jonathan Doman522377d2023-08-18 15:27:54 -070053 BMCWEB_LOG_ERROR(
54 "Missing UserPasswordExpired or UserGroups property for local user");
55 return false;
Gunnar Mills59ba6382023-08-01 12:41:17 -050056 }
Jonathan Doman522377d2023-08-18 15:27:54 -070057
Paul Fertser478c5a52024-06-26 22:27:59 +000058 session.userRole = userRole;
59 BMCWEB_LOG_DEBUG("userName = {} userRole = {}", session.username, userRole);
Gunnar Mills59ba6382023-08-01 12:41:17 -050060
61 // Set isConfigureSelfOnly based on D-Bus results. This
62 // ignores the results from both pamAuthenticateUser and the
63 // value from any previous use of this session.
Paul Fertser478c5a52024-06-26 22:27:59 +000064 session.isConfigureSelfOnly = passwordExpired.value_or(false);
Gunnar Mills59ba6382023-08-01 12:41:17 -050065
Paul Fertser478c5a52024-06-26 22:27:59 +000066 session.userGroups.clear();
Jonathan Doman522377d2023-08-18 15:27:54 -070067 if (userGroups)
Gunnar Mills59ba6382023-08-01 12:41:17 -050068 {
Paul Fertser478c5a52024-06-26 22:27:59 +000069 session.userGroups.swap(*userGroups);
Gunnar Mills59ba6382023-08-01 12:41:17 -050070 }
71
72 return true;
73}
Ed Tanous08bbe112023-04-06 13:10:02 -070074
Patrick Williamsbd79bce2024-08-16 15:22:20 -040075inline bool isUserPrivileged(
76 Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
77 BaseRule& rule)
Ed Tanous08bbe112023-04-06 13:10:02 -070078{
Ed Tanousd3dfb6e2023-07-27 09:33:49 -070079 if (req.session == nullptr)
80 {
81 return false;
82 }
Ed Tanous08bbe112023-04-06 13:10:02 -070083 // Get the user's privileges from the role
84 redfish::Privileges userPrivileges =
85 redfish::getUserPrivileges(*req.session);
86
87 // Modify privileges if isConfigureSelfOnly.
88 if (req.session->isConfigureSelfOnly)
89 {
90 // Remove all privileges except ConfigureSelf
91 userPrivileges =
92 userPrivileges.intersection(redfish::Privileges{"ConfigureSelf"});
Ed Tanous62598e32023-07-17 17:06:25 -070093 BMCWEB_LOG_DEBUG("Operation limited to ConfigureSelf");
Ed Tanous08bbe112023-04-06 13:10:02 -070094 }
95
96 if (!rule.checkPrivileges(userPrivileges))
97 {
98 asyncResp->res.result(boost::beast::http::status::forbidden);
99 if (req.session->isConfigureSelfOnly)
100 {
101 redfish::messages::passwordChangeRequired(
102 asyncResp->res,
103 boost::urls::format("/redfish/v1/AccountService/Accounts/{}",
104 req.session->username));
105 }
106 return false;
107 }
108
Gunnar Mills59ba6382023-08-01 12:41:17 -0500109 req.userRole = req.session->userRole;
Ed Tanous08bbe112023-04-06 13:10:02 -0700110 return true;
111}
112
Paul Fertser478c5a52024-06-26 22:27:59 +0000113inline bool afterGetUserInfoValidate(
114 Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
115 BaseRule& rule, const dbus::utility::DBusPropertiesMap& userInfoMap)
Gunnar Mills59ba6382023-08-01 12:41:17 -0500116{
Paul Fertser478c5a52024-06-26 22:27:59 +0000117 if (req.session == nullptr || !populateUserInfo(*req.session, userInfoMap))
Gunnar Mills59ba6382023-08-01 12:41:17 -0500118 {
119 BMCWEB_LOG_ERROR("Failed to populate user information");
120 asyncResp->res.result(
121 boost::beast::http::status::internal_server_error);
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700122 return false;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500123 }
124
125 if (!isUserPrivileged(req, asyncResp, rule))
126 {
127 // User is not privileged
128 BMCWEB_LOG_ERROR("Insufficient Privilege");
129 asyncResp->res.result(boost::beast::http::status::forbidden);
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700130 return false;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500131 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700132
133 return true;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500134}
135
Ed Tanousbb1c7d32025-02-09 09:39:19 -0800136inline void requestUserInfo(
137 const std::string& username,
138 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
139 std::move_only_function<void(const dbus::utility::DBusPropertiesMap&)>&&
140 callback)
Paul Fertser478c5a52024-06-26 22:27:59 +0000141{
142 crow::connections::systemBus->async_method_call(
Ed Tanousbb1c7d32025-02-09 09:39:19 -0800143 [asyncResp, callback = std::move(callback)](
Paul Fertser478c5a52024-06-26 22:27:59 +0000144 const boost::system::error_code& ec,
145 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400146 if (ec)
147 {
148 BMCWEB_LOG_ERROR("GetUserInfo failed...");
149 asyncResp->res.result(
150 boost::beast::http::status::internal_server_error);
151 return;
152 }
153 callback(userInfoMap);
154 },
Paul Fertser478c5a52024-06-26 22:27:59 +0000155 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
156 "xyz.openbmc_project.User.Manager", "GetUserInfo", username);
157}
158
Ed Tanousbb1c7d32025-02-09 09:39:19 -0800159inline void validatePrivilege(
160 const std::shared_ptr<Request>& req,
161 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, BaseRule& rule,
162 std::move_only_function<void()>&& callback)
Ed Tanous08bbe112023-04-06 13:10:02 -0700163{
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700164 if (req->session == nullptr)
Ed Tanous08bbe112023-04-06 13:10:02 -0700165 {
166 return;
167 }
Paul Fertser478c5a52024-06-26 22:27:59 +0000168
169 requestUserInfo(
170 req->session->username, asyncResp,
Ed Tanousbb1c7d32025-02-09 09:39:19 -0800171 [req, asyncResp, &rule, callback = std::move(callback)](
Gunnar Mills59ba6382023-08-01 12:41:17 -0500172 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400173 if (afterGetUserInfoValidate(*req, asyncResp, rule, userInfoMap))
174 {
175 callback();
176 }
177 });
Paul Fertser478c5a52024-06-26 22:27:59 +0000178}
179
Ed Tanousbb1c7d32025-02-09 09:39:19 -0800180inline void getUserInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
181 const std::string& username,
182 std::shared_ptr<persistent_data::UserSession>& session,
183 std::move_only_function<void()>&& callback)
Paul Fertser478c5a52024-06-26 22:27:59 +0000184{
185 requestUserInfo(
186 username, asyncResp,
Ed Tanousbb1c7d32025-02-09 09:39:19 -0800187 [asyncResp, session, callback = std::move(callback)](
188 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400189 if (!populateUserInfo(*session, userInfoMap))
190 {
191 BMCWEB_LOG_ERROR("Failed to populate user information");
192 asyncResp->res.result(
193 boost::beast::http::status::internal_server_error);
194 return;
195 }
196 callback();
197 });
Ed Tanous08bbe112023-04-06 13:10:02 -0700198}
199
200} // namespace crow