blob: f174991bee7059d7a47462780fe20cb6fec61a48 [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
21#include <memory>
Ed Tanousd7857202025-01-28 15:32:26 -080022#include <optional>
Paul Fertser478c5a52024-06-26 22:27:59 +000023#include <string>
24#include <utility>
Ed Tanous08bbe112023-04-06 13:10:02 -070025#include <vector>
26
27namespace crow
28{
Gunnar Mills59ba6382023-08-01 12:41:17 -050029// Populate session with user information.
30inline bool
Paul Fertser478c5a52024-06-26 22:27:59 +000031 populateUserInfo(persistent_data::UserSession& session,
Gunnar Mills59ba6382023-08-01 12:41:17 -050032 const dbus::utility::DBusPropertiesMap& userInfoMap)
33{
Jonathan Doman522377d2023-08-18 15:27:54 -070034 std::string userRole;
35 bool remoteUser = false;
36 std::optional<bool> passwordExpired;
37 std::optional<std::vector<std::string>> userGroups;
Gunnar Mills59ba6382023-08-01 12:41:17 -050038
Jonathan Doman522377d2023-08-18 15:27:54 -070039 const bool success = sdbusplus::unpackPropertiesNoThrow(
40 redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap, "UserPrivilege",
41 userRole, "RemoteUser", remoteUser, "UserPasswordExpired",
42 passwordExpired, "UserGroups", userGroups);
43
44 if (!success)
Gunnar Mills59ba6382023-08-01 12:41:17 -050045 {
Jonathan Doman522377d2023-08-18 15:27:54 -070046 BMCWEB_LOG_ERROR("Failed to unpack user properties.");
Gunnar Mills59ba6382023-08-01 12:41:17 -050047 return false;
48 }
Jonathan Doman522377d2023-08-18 15:27:54 -070049
50 if (!remoteUser && (!passwordExpired || !userGroups))
Gunnar Mills59ba6382023-08-01 12:41:17 -050051 {
Jonathan Doman522377d2023-08-18 15:27:54 -070052 BMCWEB_LOG_ERROR(
53 "Missing UserPasswordExpired or UserGroups property for local user");
54 return false;
Gunnar Mills59ba6382023-08-01 12:41:17 -050055 }
Jonathan Doman522377d2023-08-18 15:27:54 -070056
Paul Fertser478c5a52024-06-26 22:27:59 +000057 session.userRole = userRole;
58 BMCWEB_LOG_DEBUG("userName = {} userRole = {}", session.username, userRole);
Gunnar Mills59ba6382023-08-01 12:41:17 -050059
60 // Set isConfigureSelfOnly based on D-Bus results. This
61 // ignores the results from both pamAuthenticateUser and the
62 // value from any previous use of this session.
Paul Fertser478c5a52024-06-26 22:27:59 +000063 session.isConfigureSelfOnly = passwordExpired.value_or(false);
Gunnar Mills59ba6382023-08-01 12:41:17 -050064
Paul Fertser478c5a52024-06-26 22:27:59 +000065 session.userGroups.clear();
Jonathan Doman522377d2023-08-18 15:27:54 -070066 if (userGroups)
Gunnar Mills59ba6382023-08-01 12:41:17 -050067 {
Paul Fertser478c5a52024-06-26 22:27:59 +000068 session.userGroups.swap(*userGroups);
Gunnar Mills59ba6382023-08-01 12:41:17 -050069 }
70
71 return true;
72}
Ed Tanous08bbe112023-04-06 13:10:02 -070073
Patrick Williamsbd79bce2024-08-16 15:22:20 -040074inline bool isUserPrivileged(
75 Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
76 BaseRule& rule)
Ed Tanous08bbe112023-04-06 13:10:02 -070077{
Ed Tanousd3dfb6e2023-07-27 09:33:49 -070078 if (req.session == nullptr)
79 {
80 return false;
81 }
Ed Tanous08bbe112023-04-06 13:10:02 -070082 // Get the user's privileges from the role
83 redfish::Privileges userPrivileges =
84 redfish::getUserPrivileges(*req.session);
85
86 // Modify privileges if isConfigureSelfOnly.
87 if (req.session->isConfigureSelfOnly)
88 {
89 // Remove all privileges except ConfigureSelf
90 userPrivileges =
91 userPrivileges.intersection(redfish::Privileges{"ConfigureSelf"});
Ed Tanous62598e32023-07-17 17:06:25 -070092 BMCWEB_LOG_DEBUG("Operation limited to ConfigureSelf");
Ed Tanous08bbe112023-04-06 13:10:02 -070093 }
94
95 if (!rule.checkPrivileges(userPrivileges))
96 {
97 asyncResp->res.result(boost::beast::http::status::forbidden);
98 if (req.session->isConfigureSelfOnly)
99 {
100 redfish::messages::passwordChangeRequired(
101 asyncResp->res,
102 boost::urls::format("/redfish/v1/AccountService/Accounts/{}",
103 req.session->username));
104 }
105 return false;
106 }
107
Gunnar Mills59ba6382023-08-01 12:41:17 -0500108 req.userRole = req.session->userRole;
Ed Tanous08bbe112023-04-06 13:10:02 -0700109 return true;
110}
111
Paul Fertser478c5a52024-06-26 22:27:59 +0000112inline bool afterGetUserInfoValidate(
113 Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
114 BaseRule& rule, const dbus::utility::DBusPropertiesMap& userInfoMap)
Gunnar Mills59ba6382023-08-01 12:41:17 -0500115{
Paul Fertser478c5a52024-06-26 22:27:59 +0000116 if (req.session == nullptr || !populateUserInfo(*req.session, userInfoMap))
Gunnar Mills59ba6382023-08-01 12:41:17 -0500117 {
118 BMCWEB_LOG_ERROR("Failed to populate user information");
119 asyncResp->res.result(
120 boost::beast::http::status::internal_server_error);
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700121 return false;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500122 }
123
124 if (!isUserPrivileged(req, asyncResp, rule))
125 {
126 // User is not privileged
127 BMCWEB_LOG_ERROR("Insufficient Privilege");
128 asyncResp->res.result(boost::beast::http::status::forbidden);
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700129 return false;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500130 }
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700131
132 return true;
Gunnar Mills59ba6382023-08-01 12:41:17 -0500133}
134
135template <typename CallbackFn>
Paul Fertser478c5a52024-06-26 22:27:59 +0000136void requestUserInfo(const std::string& username,
137 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
138 CallbackFn&& callback)
139{
140 crow::connections::systemBus->async_method_call(
141 [asyncResp, callback = std::forward<CallbackFn>(callback)](
142 const boost::system::error_code& ec,
143 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400144 if (ec)
145 {
146 BMCWEB_LOG_ERROR("GetUserInfo failed...");
147 asyncResp->res.result(
148 boost::beast::http::status::internal_server_error);
149 return;
150 }
151 callback(userInfoMap);
152 },
Paul Fertser478c5a52024-06-26 22:27:59 +0000153 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
154 "xyz.openbmc_project.User.Manager", "GetUserInfo", username);
155}
156
157template <typename CallbackFn>
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700158void validatePrivilege(const std::shared_ptr<Request>& req,
Ed Tanous08bbe112023-04-06 13:10:02 -0700159 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
160 BaseRule& rule, CallbackFn&& callback)
161{
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700162 if (req->session == nullptr)
Ed Tanous08bbe112023-04-06 13:10:02 -0700163 {
164 return;
165 }
Paul Fertser478c5a52024-06-26 22:27:59 +0000166
167 requestUserInfo(
168 req->session->username, asyncResp,
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700169 [req, asyncResp, &rule, callback = std::forward<CallbackFn>(callback)](
Gunnar Mills59ba6382023-08-01 12:41:17 -0500170 const dbus::utility::DBusPropertiesMap& userInfoMap) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400171 if (afterGetUserInfoValidate(*req, asyncResp, rule, userInfoMap))
172 {
173 callback();
174 }
175 });
Paul Fertser478c5a52024-06-26 22:27:59 +0000176}
177
178template <typename CallbackFn>
179void getUserInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
180 const std::string& username,
181 std::shared_ptr<persistent_data::UserSession>& session,
182 CallbackFn&& callback)
183{
184 requestUserInfo(
185 username, asyncResp,
186 [asyncResp, session, callback = std::forward<CallbackFn>(callback)](
187 const dbus::utility::DBusPropertiesMap& userInfoMap) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400188 if (!populateUserInfo(*session, userInfoMap))
189 {
190 BMCWEB_LOG_ERROR("Failed to populate user information");
191 asyncResp->res.result(
192 boost::beast::http::status::internal_server_error);
193 return;
194 }
195 callback();
196 });
Ed Tanous08bbe112023-04-06 13:10:02 -0700197}
198
199} // namespace crow