blob: 3e9983e6d029b41d21dbbd9585b3804e1cf2a821 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
3// SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01004#pragma once
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01005
Ed Tanousd7857202025-01-28 15:32:26 -08006#include "bmcweb_config.h"
7
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08008#include "app.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08009#include "async_resp.hpp"
Gunnar Millsa0735a42024-09-06 12:51:11 -050010#include "boost_formatters.hpp"
Ed Tanous1aa375b2024-04-13 11:51:10 -070011#include "certificate_service.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080012#include "dbus_utility.hpp"
13#include "error_messages.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070014#include "generated/enums/account_service.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080015#include "http_request.hpp"
16#include "http_response.hpp"
17#include "logging.hpp"
18#include "pam_authenticate.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080019#include "persistent_data.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080020#include "privileges.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080021#include "query.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070022#include "registries/privilege_registry.hpp"
Ed Tanous3281bcf2024-06-25 16:02:05 -070023#include "sessions.hpp"
Ed Tanous1aa375b2024-04-13 11:51:10 -070024#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080025#include "utils/dbus_utils.hpp"
26#include "utils/json_utils.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070027
Ed Tanousd7857202025-01-28 15:32:26 -080028#include <security/_pam_types.h>
29#include <systemd/sd-bus.h>
30
31#include <boost/beast/http/field.hpp>
32#include <boost/beast/http/status.hpp>
33#include <boost/beast/http/verb.hpp>
Ed Tanous1aa375b2024-04-13 11:51:10 -070034#include <boost/url/format.hpp>
35#include <boost/url/url.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080036#include <nlohmann/json.hpp>
37#include <sdbusplus/message.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020038#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039
Ed Tanousd7857202025-01-28 15:32:26 -080040#include <algorithm>
George Liu2b731192023-01-11 16:27:13 +080041#include <array>
Ed Tanousd7857202025-01-28 15:32:26 -080042#include <cstddef>
43#include <cstdint>
44#include <cstring>
45#include <format>
46#include <functional>
Ed Tanous1aa375b2024-04-13 11:51:10 -070047#include <memory>
Abhishek Patelc7229812022-02-01 10:07:15 -060048#include <optional>
Ed Tanous3544d2a2023-08-06 18:12:20 -070049#include <ranges>
Abhishek Patelc7229812022-02-01 10:07:15 -060050#include <string>
George Liu2b731192023-01-11 16:27:13 +080051#include <string_view>
Ed Tanous20fa6a22024-05-20 18:02:58 -070052#include <utility>
Ed Tanousd7857202025-01-28 15:32:26 -080053#include <variant>
Abhishek Patelc7229812022-02-01 10:07:15 -060054#include <vector>
55
Ed Tanous1abe55e2018-09-05 08:30:59 -070056namespace redfish
57{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010058
Ed Tanous23a21a12020-07-25 04:45:05 +000059constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053060 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070061constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053062 "/xyz/openbmc_project/user/ldap/active_directory";
63
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053064constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053065constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
66constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
67constexpr const char* ldapConfigInterface =
68 "xyz.openbmc_project.User.Ldap.Config";
Ratan Gupta6973a582018-12-13 18:25:44 +053069constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053070constexpr const char* ldapPrivMapperInterface =
71 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053072
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060073struct LDAPRoleMapData
74{
75 std::string groupName;
76 std::string privilege;
77};
78
Ratan Gupta6973a582018-12-13 18:25:44 +053079struct LDAPConfigData
80{
Ed Tanous47f29342024-03-19 12:18:06 -070081 std::string uri;
82 std::string bindDN;
83 std::string baseDN;
84 std::string searchScope;
85 std::string serverType;
Ratan Gupta6973a582018-12-13 18:25:44 +053086 bool serviceEnabled = false;
Ed Tanous47f29342024-03-19 12:18:06 -070087 std::string userNameAttribute;
88 std::string groupAttribute;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060089 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053090};
91
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060092inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053093{
94 if (role == "priv-admin")
95 {
96 return "Administrator";
97 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070098 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053099 {
AppaRao Pulic80fee52019-10-16 14:49:36 +0530100 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530101 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700102 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530103 {
104 return "Operator";
105 }
106 return "";
107}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600108inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530109{
110 if (role == "Administrator")
111 {
112 return "priv-admin";
113 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700114 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530115 {
116 return "priv-user";
117 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700118 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530119 {
120 return "priv-operator";
121 }
122 return "";
123}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700124
Abhishek Patelc7229812022-02-01 10:07:15 -0600125/**
126 * @brief Maps user group names retrieved from D-Bus object to
127 * Account Types.
128 *
129 * @param[in] userGroups List of User groups
130 * @param[out] res AccountTypes populated
131 *
132 * @return true in case of success, false if UserGroups contains
133 * invalid group name(s).
134 */
135inline bool translateUserGroup(const std::vector<std::string>& userGroups,
136 crow::Response& res)
137{
138 std::vector<std::string> accountTypes;
139 for (const auto& userGroup : userGroups)
140 {
141 if (userGroup == "redfish")
142 {
143 accountTypes.emplace_back("Redfish");
144 accountTypes.emplace_back("WebUI");
145 }
146 else if (userGroup == "ipmi")
147 {
148 accountTypes.emplace_back("IPMI");
149 }
150 else if (userGroup == "ssh")
151 {
Abhishek Patelc7229812022-02-01 10:07:15 -0600152 accountTypes.emplace_back("ManagerConsole");
153 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500154 else if (userGroup == "hostconsole")
155 {
156 // The hostconsole group controls who can access the host console
157 // port via ssh and websocket.
158 accountTypes.emplace_back("HostConsole");
159 }
Abhishek Patelc7229812022-02-01 10:07:15 -0600160 else if (userGroup == "web")
161 {
162 // 'web' is one of the valid groups in the UserGroups property of
163 // the user account in the D-Bus object. This group is currently not
164 // doing anything, and is considered to be equivalent to 'redfish'.
165 // 'redfish' user group is mapped to 'Redfish'and 'WebUI'
166 // AccountTypes, so do nothing here...
167 }
168 else
169 {
Ed Tanous8ece0e42024-01-02 13:16:50 -0800170 // Invalid user group name. Caller throws an exception.
Abhishek Patelc7229812022-02-01 10:07:15 -0600171 return false;
172 }
173 }
174
175 res.jsonValue["AccountTypes"] = std::move(accountTypes);
176 return true;
177}
178
Abhishek Patel58345852022-02-02 08:54:25 -0600179/**
180 * @brief Builds User Groups from the Account Types
181 *
182 * @param[in] asyncResp Async Response
183 * @param[in] accountTypes List of Account Types
184 * @param[out] userGroups List of User Groups mapped from Account Types
185 *
186 * @return true if Account Types mapped to User Groups, false otherwise.
187 */
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400188inline bool getUserGroupFromAccountType(
189 crow::Response& res, const std::vector<std::string>& accountTypes,
190 std::vector<std::string>& userGroups)
Abhishek Patel58345852022-02-02 08:54:25 -0600191{
192 // Need both Redfish and WebUI Account Types to map to 'redfish' User Group
193 bool redfishType = false;
194 bool webUIType = false;
195
196 for (const auto& accountType : accountTypes)
197 {
198 if (accountType == "Redfish")
199 {
200 redfishType = true;
201 }
202 else if (accountType == "WebUI")
203 {
204 webUIType = true;
205 }
206 else if (accountType == "IPMI")
207 {
208 userGroups.emplace_back("ipmi");
209 }
210 else if (accountType == "HostConsole")
211 {
212 userGroups.emplace_back("hostconsole");
213 }
214 else if (accountType == "ManagerConsole")
215 {
216 userGroups.emplace_back("ssh");
217 }
218 else
219 {
220 // Invalid Account Type
221 messages::propertyValueNotInList(res, "AccountTypes", accountType);
222 return false;
223 }
224 }
225
226 // Both Redfish and WebUI Account Types are needed to PATCH
227 if (redfishType ^ webUIType)
228 {
Ed Tanous62598e32023-07-17 17:06:25 -0700229 BMCWEB_LOG_ERROR(
230 "Missing Redfish or WebUI Account Type to set redfish User Group");
Abhishek Patel58345852022-02-02 08:54:25 -0600231 messages::strictAccountTypes(res, "AccountTypes");
232 return false;
233 }
234
235 if (redfishType && webUIType)
236 {
237 userGroups.emplace_back("redfish");
238 }
239
240 return true;
241}
242
243/**
244 * @brief Sets UserGroups property of the user based on the Account Types
245 *
246 * @param[in] accountTypes List of User Account Types
247 * @param[in] asyncResp Async Response
248 * @param[in] dbusObjectPath D-Bus Object Path
249 * @param[in] userSelf true if User is updating OWN Account Types
250 */
Patrick Williams504af5a2025-02-03 14:29:03 -0500251inline void patchAccountTypes(
252 const std::vector<std::string>& accountTypes,
253 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
254 const std::string& dbusObjectPath, bool userSelf)
Abhishek Patel58345852022-02-02 08:54:25 -0600255{
256 // Check if User is disabling own Redfish Account Type
257 if (userSelf &&
258 (accountTypes.cend() ==
259 std::find(accountTypes.cbegin(), accountTypes.cend(), "Redfish")))
260 {
Ed Tanous62598e32023-07-17 17:06:25 -0700261 BMCWEB_LOG_ERROR(
262 "User disabling OWN Redfish Account Type is not allowed");
Abhishek Patel58345852022-02-02 08:54:25 -0600263 messages::strictAccountTypes(asyncResp->res, "AccountTypes");
264 return;
265 }
266
267 std::vector<std::string> updatedUserGroups;
268 if (!getUserGroupFromAccountType(asyncResp->res, accountTypes,
269 updatedUserGroups))
270 {
271 // Problem in mapping Account Types to User Groups, Error already
272 // logged.
273 return;
274 }
Ginu Georgee93abac2024-06-14 17:35:27 +0530275 setDbusProperty(asyncResp, "AccountTypes",
276 "xyz.openbmc_project.User.Manager", dbusObjectPath,
277 "xyz.openbmc_project.User.Attributes", "UserGroups",
278 updatedUserGroups);
Abhishek Patel58345852022-02-02 08:54:25 -0600279}
280
zhanghch058d1b46d2021-04-01 11:18:24 +0800281inline void userErrorMessageHandler(
282 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
283 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000284{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000285 if (e == nullptr)
286 {
287 messages::internalError(asyncResp->res);
288 return;
289 }
290
Manojkiran Eda055806b2020-11-03 09:36:28 +0530291 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000292 if (strcmp(errorMessage,
293 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
294 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800295 messages::resourceAlreadyExists(asyncResp->res, "ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000296 "UserName", newUser);
297 }
298 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
299 "UserNameDoesNotExist") == 0)
300 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800301 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000302 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700303 else if ((strcmp(errorMessage,
304 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
305 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800306 (strcmp(
307 errorMessage,
308 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
309 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000310 {
311 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
312 }
313 else if (strcmp(errorMessage,
314 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
315 {
316 messages::createLimitReachedForResource(asyncResp->res);
317 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000318 else
319 {
Gunnar Millsb8ad5832023-10-02 16:26:07 -0500320 BMCWEB_LOG_ERROR("DBUS response error {}", errorMessage);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000321 messages::internalError(asyncResp->res);
322 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000323}
324
Ed Tanous81ce6092020-12-17 16:54:55 +0000325inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000326 const LDAPConfigData& confData,
327 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530328{
Ed Tanous49cc2632024-03-20 12:49:15 -0700329 nlohmann::json::object_t ldap;
Ed Tanous14766872022-03-15 10:44:42 -0700330 ldap["ServiceEnabled"] = confData.serviceEnabled;
Ed Tanous49cc2632024-03-20 12:49:15 -0700331 nlohmann::json::array_t serviceAddresses;
Ed Tanous092a33f2025-07-01 11:56:12 -0700332 if (!confData.uri.empty())
333 {
334 serviceAddresses.emplace_back(confData.uri);
335 }
Ed Tanous49cc2632024-03-20 12:49:15 -0700336 ldap["ServiceAddresses"] = std::move(serviceAddresses);
337
338 nlohmann::json::object_t authentication;
339 authentication["AuthenticationType"] =
Ed Tanous0ec8b832022-03-14 14:56:47 -0700340 account_service::AuthenticationTypes::UsernameAndPassword;
Ed Tanous49cc2632024-03-20 12:49:15 -0700341 authentication["Username"] = confData.bindDN;
342 authentication["Password"] = nullptr;
343 ldap["Authentication"] = std::move(authentication);
Ed Tanous14766872022-03-15 10:44:42 -0700344
Ed Tanous49cc2632024-03-20 12:49:15 -0700345 nlohmann::json::object_t ldapService;
346 nlohmann::json::object_t searchSettings;
347 nlohmann::json::array_t baseDistinguishedNames;
Ed Tanous092a33f2025-07-01 11:56:12 -0700348 if (!confData.baseDN.empty())
349 {
350 baseDistinguishedNames.emplace_back(confData.baseDN);
351 }
Ed Tanous14766872022-03-15 10:44:42 -0700352
Ed Tanous49cc2632024-03-20 12:49:15 -0700353 searchSettings["BaseDistinguishedNames"] =
354 std::move(baseDistinguishedNames);
355 searchSettings["UsernameAttribute"] = confData.userNameAttribute;
356 searchSettings["GroupsAttribute"] = confData.groupAttribute;
357 ldapService["SearchSettings"] = std::move(searchSettings);
358 ldap["LDAPService"] = std::move(ldapService);
359
360 nlohmann::json::array_t roleMapArray;
Ed Tanous9eb808c2022-01-25 10:19:23 -0800361 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600362 {
Ed Tanous62598e32023-07-17 17:06:25 -0700363 BMCWEB_LOG_DEBUG("Pushing the data groupName={}", obj.second.groupName);
Ed Tanous613dabe2022-07-09 11:17:36 -0700364
Ed Tanous613dabe2022-07-09 11:17:36 -0700365 nlohmann::json::object_t remoteGroup;
366 remoteGroup["RemoteGroup"] = obj.second.groupName;
Jorge Cisneros329f0342022-11-04 16:26:25 +0000367 remoteGroup["LocalRole"] = getRoleIdFromPrivilege(obj.second.privilege);
368 roleMapArray.emplace_back(std::move(remoteGroup));
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600369 }
Ed Tanous49cc2632024-03-20 12:49:15 -0700370
371 ldap["RemoteRoleMapping"] = std::move(roleMapArray);
372
373 jsonResponse[ldapType].update(ldap);
Ratan Gupta6973a582018-12-13 18:25:44 +0530374}
375
376/**
Ratan Gupta06785242019-07-26 22:30:16 +0530377 * @brief validates given JSON input and then calls appropriate method to
378 * create, to delete or to set Rolemapping object based on the given input.
379 *
380 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000381inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800382 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530383 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousc1019822024-03-06 12:54:38 -0800384 const std::string& serverType,
385 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530386{
387 for (size_t index = 0; index < input.size(); index++)
388 {
Ed Tanousc1019822024-03-06 12:54:38 -0800389 std::variant<nlohmann::json::object_t, std::nullptr_t>& thisJson =
390 input[index];
391 nlohmann::json::object_t* obj =
392 std::get_if<nlohmann::json::object_t>(&thisJson);
393 if (obj == nullptr)
Ratan Gupta06785242019-07-26 22:30:16 +0530394 {
395 // delete the existing object
396 if (index < roleMapObjData.size())
397 {
Ed Tanous177612a2025-02-14 15:16:09 -0800398 dbus::utility::async_method_call(
399 asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530400 [asyncResp, roleMapObjData, serverType,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800401 index](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400402 if (ec)
403 {
404 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
405 messages::internalError(asyncResp->res);
406 return;
407 }
408 asyncResp->res
409 .jsonValue[serverType]["RemoteRoleMapping"][index] =
410 nullptr;
411 },
Ratan Gupta06785242019-07-26 22:30:16 +0530412 ldapDbusService, roleMapObjData[index].first,
413 "xyz.openbmc_project.Object.Delete", "Delete");
414 }
415 else
416 {
Ed Tanous62598e32023-07-17 17:06:25 -0700417 BMCWEB_LOG_ERROR("Can't delete the object");
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400418 messages::propertyValueTypeError(
419 asyncResp->res, "null",
420 "RemoteRoleMapping/" + std::to_string(index));
Ratan Gupta06785242019-07-26 22:30:16 +0530421 return;
422 }
423 }
Ed Tanousc1019822024-03-06 12:54:38 -0800424 else if (obj->empty())
Ratan Gupta06785242019-07-26 22:30:16 +0530425 {
426 // Don't do anything for the empty objects,parse next json
427 // eg {"RemoteRoleMapping",[{}]}
428 }
429 else
430 {
431 // update/create the object
432 std::optional<std::string> remoteGroup;
433 std::optional<std::string> localRole;
434
Patrick Williams504af5a2025-02-03 14:29:03 -0500435 if (!json_util::readJsonObject( //
436 *obj, asyncResp->res, //
437 "LocalRole", localRole, //
Myung Baeafc474a2024-10-09 00:53:29 -0700438 "RemoteGroup", remoteGroup //
439 ))
Ratan Gupta06785242019-07-26 22:30:16 +0530440 {
441 continue;
442 }
443
444 // Update existing RoleMapping Object
445 if (index < roleMapObjData.size())
446 {
Ed Tanous62598e32023-07-17 17:06:25 -0700447 BMCWEB_LOG_DEBUG("Update Role Map Object");
Ratan Gupta06785242019-07-26 22:30:16 +0530448 // If "RemoteGroup" info is provided
449 if (remoteGroup)
450 {
Ed Tanousd02aad32024-02-13 14:43:34 -0800451 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530452 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800453 std::format("RemoteRoleMapping/{}/RemoteGroup", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530454 ldapDbusService, roleMapObjData[index].first,
455 "xyz.openbmc_project.User.PrivilegeMapperEntry",
456 "GroupName", *remoteGroup);
Ratan Gupta06785242019-07-26 22:30:16 +0530457 }
458
459 // If "LocalRole" info is provided
460 if (localRole)
461 {
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500462 std::string priv = getPrivilegeFromRoleId(*localRole);
463 if (priv.empty())
464 {
465 messages::propertyValueNotInList(
466 asyncResp->res, *localRole,
467 std::format("RemoteRoleMapping/{}/LocalRole",
468 index));
469 return;
470 }
Ed Tanousd02aad32024-02-13 14:43:34 -0800471 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530472 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800473 std::format("RemoteRoleMapping/{}/LocalRole", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530474 ldapDbusService, roleMapObjData[index].first,
475 "xyz.openbmc_project.User.PrivilegeMapperEntry",
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500476 "Privilege", priv);
Ratan Gupta06785242019-07-26 22:30:16 +0530477 }
478 }
479 // Create a new RoleMapping Object.
480 else
481 {
Ed Tanous62598e32023-07-17 17:06:25 -0700482 BMCWEB_LOG_DEBUG(
483 "setRoleMappingProperties: Creating new Object");
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400484 std::string pathString =
485 "RemoteRoleMapping/" + std::to_string(index);
Ratan Gupta06785242019-07-26 22:30:16 +0530486
487 if (!localRole)
488 {
489 messages::propertyMissing(asyncResp->res,
490 pathString + "/LocalRole");
491 continue;
492 }
493 if (!remoteGroup)
494 {
495 messages::propertyMissing(asyncResp->res,
496 pathString + "/RemoteGroup");
497 continue;
498 }
499
500 std::string dbusObjectPath;
501 if (serverType == "ActiveDirectory")
502 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700503 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530504 }
505 else if (serverType == "LDAP")
506 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000507 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530508 }
509
Ed Tanous62598e32023-07-17 17:06:25 -0700510 BMCWEB_LOG_DEBUG("Remote Group={},LocalRole={}", *remoteGroup,
511 *localRole);
Ratan Gupta06785242019-07-26 22:30:16 +0530512
Ed Tanous177612a2025-02-14 15:16:09 -0800513 dbus::utility::async_method_call(
514 asyncResp,
Ed Tanous271584a2019-07-09 16:24:22 -0700515 [asyncResp, serverType, localRole,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800516 remoteGroup](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400517 if (ec)
518 {
519 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
520 messages::internalError(asyncResp->res);
521 return;
522 }
523 nlohmann::json& remoteRoleJson =
524 asyncResp->res
525 .jsonValue[serverType]["RemoteRoleMapping"];
526 nlohmann::json::object_t roleMapEntry;
527 roleMapEntry["LocalRole"] = *localRole;
528 roleMapEntry["RemoteGroup"] = *remoteGroup;
529 remoteRoleJson.emplace_back(std::move(roleMapEntry));
530 },
Ratan Gupta06785242019-07-26 22:30:16 +0530531 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700532 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530533 getPrivilegeFromRoleId(std::move(*localRole)));
534 }
535 }
536 }
537}
538
539/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530540 * Function that retrieves all properties for LDAP config object
541 * into JSON
542 */
543template <typename CallbackFunc>
Patrick Williams504af5a2025-02-03 14:29:03 -0500544inline void getLDAPConfigData(const std::string& ldapType,
545 CallbackFunc&& callback)
Ratan Gupta6973a582018-12-13 18:25:44 +0530546{
George Liu2b731192023-01-11 16:27:13 +0800547 constexpr std::array<std::string_view, 2> interfaces = {
548 ldapEnableInterface, ldapConfigInterface};
Ratan Gupta6973a582018-12-13 18:25:44 +0530549
George Liu2b731192023-01-11 16:27:13 +0800550 dbus::utility::getDbusObject(
551 ldapConfigObjectName, interfaces,
Ed Tanous8cb2c022024-03-27 16:31:46 -0700552 [callback = std::forward<CallbackFunc>(callback),
Ed Tanousc1019822024-03-06 12:54:38 -0800553 ldapType](const boost::system::error_code& ec,
554 const dbus::utility::MapperGetObject& resp) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400555 if (ec || resp.empty())
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600556 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400557 BMCWEB_LOG_WARNING(
558 "DBUS response error during getting of service name: {}",
559 ec);
560 LDAPConfigData empty{};
561 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600562 return;
563 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400564 std::string service = resp.begin()->first;
565 sdbusplus::message::object_path path(ldapRootObject);
566 dbus::utility::getManagedObjects(
567 service, path,
568 [callback, ldapType](const boost::system::error_code& ec2,
569 const dbus::utility::ManagedObjectType&
570 ldapObjects) mutable {
571 LDAPConfigData confData{};
572 if (ec2)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600573 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400574 callback(false, confData, ldapType);
575 BMCWEB_LOG_WARNING("D-Bus responses error: {}", ec2);
576 return;
577 }
578
579 std::string ldapDbusType;
580 std::string searchString;
581
582 if (ldapType == "LDAP")
583 {
584 ldapDbusType =
585 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
586 searchString = "openldap";
587 }
588 else if (ldapType == "ActiveDirectory")
589 {
590 ldapDbusType =
591 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
592 searchString = "active_directory";
593 }
594 else
595 {
596 BMCWEB_LOG_ERROR(
597 "Can't get the DbusType for the given type={}",
598 ldapType);
599 callback(false, confData, ldapType);
600 return;
601 }
602
603 std::string ldapEnableInterfaceStr = ldapEnableInterface;
604 std::string ldapConfigInterfaceStr = ldapConfigInterface;
605
606 for (const auto& object : ldapObjects)
607 {
608 // let's find the object whose ldap type is equal to the
609 // given type
610 if (object.first.str.find(searchString) ==
611 std::string::npos)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600612 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400613 continue;
614 }
615
616 for (const auto& interface : object.second)
617 {
618 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600619 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400620 // rest of the properties are string.
621 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600622 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400623 if (property.first == "Enabled")
624 {
625 const bool* value =
626 std::get_if<bool>(&property.second);
627 if (value == nullptr)
628 {
629 continue;
630 }
631 confData.serviceEnabled = *value;
632 break;
633 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600634 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400635 }
636 else if (interface.first == ldapConfigInterfaceStr)
637 {
638 for (const auto& property : interface.second)
639 {
640 const std::string* strValue =
641 std::get_if<std::string>(
642 &property.second);
643 if (strValue == nullptr)
644 {
645 continue;
646 }
647 if (property.first == "LDAPServerURI")
648 {
649 confData.uri = *strValue;
650 }
651 else if (property.first == "LDAPBindDN")
652 {
653 confData.bindDN = *strValue;
654 }
655 else if (property.first == "LDAPBaseDN")
656 {
657 confData.baseDN = *strValue;
658 }
659 else if (property.first ==
660 "LDAPSearchScope")
661 {
662 confData.searchScope = *strValue;
663 }
664 else if (property.first ==
665 "GroupNameAttribute")
666 {
667 confData.groupAttribute = *strValue;
668 }
669 else if (property.first ==
670 "UserNameAttribute")
671 {
672 confData.userNameAttribute = *strValue;
673 }
674 else if (property.first == "LDAPType")
675 {
676 confData.serverType = *strValue;
677 }
678 }
679 }
680 else if (
681 interface.first ==
682 "xyz.openbmc_project.User.PrivilegeMapperEntry")
683 {
684 LDAPRoleMapData roleMapData{};
685 for (const auto& property : interface.second)
686 {
687 const std::string* strValue =
688 std::get_if<std::string>(
689 &property.second);
690
691 if (strValue == nullptr)
692 {
693 continue;
694 }
695
696 if (property.first == "GroupName")
697 {
698 roleMapData.groupName = *strValue;
699 }
700 else if (property.first == "Privilege")
701 {
702 roleMapData.privilege = *strValue;
703 }
704 }
705
706 confData.groupRoleList.emplace_back(
707 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600708 }
709 }
710 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400711 callback(true, confData, ldapType);
712 });
George Liu2b731192023-01-11 16:27:13 +0800713 });
Ratan Gupta6973a582018-12-13 18:25:44 +0530714}
715
Ed Tanous6c51eab2021-06-03 12:30:29 -0700716/**
Ed Tanous6c51eab2021-06-03 12:30:29 -0700717 * @brief updates the LDAP server address and updates the
718 json response with the new value.
719 * @param serviceAddressList address to be updated.
720 * @param asyncResp pointer to the JSON response
721 * @param ldapServerElementName Type of LDAP
722 server(openLDAP/ActiveDirectory)
723 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530724
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700725inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700726 const std::vector<std::string>& serviceAddressList,
727 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
728 const std::string& ldapServerElementName,
729 const std::string& ldapConfigObject)
730{
Ginu Georgee93abac2024-06-14 17:35:27 +0530731 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceAddress",
732 ldapDbusService, ldapConfigObject, ldapConfigInterface,
733 "LDAPServerURI", serviceAddressList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700734}
735/**
736 * @brief updates the LDAP Bind DN and updates the
737 json response with the new value.
738 * @param username name of the user which needs to be updated.
739 * @param asyncResp pointer to the JSON response
740 * @param ldapServerElementName Type of LDAP
741 server(openLDAP/ActiveDirectory)
742 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530743
Patrick Williams504af5a2025-02-03 14:29:03 -0500744inline void handleUserNamePatch(
745 const std::string& username,
746 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
747 const std::string& ldapServerElementName,
748 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700749{
Ginu Georgee93abac2024-06-14 17:35:27 +0530750 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800751 ldapServerElementName + "/Authentication/Username",
Ginu Georgee93abac2024-06-14 17:35:27 +0530752 ldapDbusService, ldapConfigObject, ldapConfigInterface,
753 "LDAPBindDN", username);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700754}
755
756/**
757 * @brief updates the LDAP password
758 * @param password : ldap password which needs to be updated.
759 * @param asyncResp pointer to the JSON response
760 * @param ldapServerElementName Type of LDAP
761 * server(openLDAP/ActiveDirectory)
762 */
763
Patrick Williams504af5a2025-02-03 14:29:03 -0500764inline void handlePasswordPatch(
765 const std::string& password,
766 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
767 const std::string& ldapServerElementName,
768 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700769{
Ginu Georgee93abac2024-06-14 17:35:27 +0530770 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800771 ldapServerElementName + "/Authentication/Password",
Ginu Georgee93abac2024-06-14 17:35:27 +0530772 ldapDbusService, ldapConfigObject, ldapConfigInterface,
773 "LDAPBindDNPassword", password);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700774}
775
776/**
777 * @brief updates the LDAP BaseDN and updates the
778 json response with the new value.
779 * @param baseDNList baseDN list which needs to be updated.
780 * @param asyncResp pointer to the JSON response
781 * @param ldapServerElementName Type of LDAP
782 server(openLDAP/ActiveDirectory)
783 */
784
Patrick Williams504af5a2025-02-03 14:29:03 -0500785inline void handleBaseDNPatch(
786 const std::vector<std::string>& baseDNList,
787 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
788 const std::string& ldapServerElementName,
789 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700790{
Ginu Georgee93abac2024-06-14 17:35:27 +0530791 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800792 ldapServerElementName +
793 "/LDAPService/SearchSettings/BaseDistinguishedNames",
Ginu Georgee93abac2024-06-14 17:35:27 +0530794 ldapDbusService, ldapConfigObject, ldapConfigInterface,
795 "LDAPBaseDN", baseDNList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700796}
797/**
798 * @brief updates the LDAP user name attribute and updates the
799 json response with the new value.
800 * @param userNameAttribute attribute to be updated.
801 * @param asyncResp pointer to the JSON response
802 * @param ldapServerElementName Type of LDAP
803 server(openLDAP/ActiveDirectory)
804 */
805
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400806inline void handleUserNameAttrPatch(
807 const std::string& userNameAttribute,
808 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
809 const std::string& ldapServerElementName,
810 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700811{
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400812 setDbusProperty(
813 asyncResp,
814 ldapServerElementName + "LDAPService/SearchSettings/UsernameAttribute",
815 ldapDbusService, ldapConfigObject, ldapConfigInterface,
816 "UserNameAttribute", userNameAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700817}
818/**
819 * @brief updates the LDAP group attribute and updates the
820 json response with the new value.
821 * @param groupsAttribute attribute to be updated.
822 * @param asyncResp pointer to the JSON response
823 * @param ldapServerElementName Type of LDAP
824 server(openLDAP/ActiveDirectory)
825 */
826
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700827inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700828 const std::string& groupsAttribute,
829 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
830 const std::string& ldapServerElementName,
831 const std::string& ldapConfigObject)
832{
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400833 setDbusProperty(
834 asyncResp,
835 ldapServerElementName + "/LDAPService/SearchSettings/GroupsAttribute",
836 ldapDbusService, ldapConfigObject, ldapConfigInterface,
837 "GroupNameAttribute", groupsAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700838}
839/**
840 * @brief updates the LDAP service enable and updates the
841 json response with the new value.
842 * @param input JSON data.
843 * @param asyncResp pointer to the JSON response
844 * @param ldapServerElementName Type of LDAP
845 server(openLDAP/ActiveDirectory)
846 */
847
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700848inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700849 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
850 const std::string& ldapServerElementName,
851 const std::string& ldapConfigObject)
852{
Ginu Georgee93abac2024-06-14 17:35:27 +0530853 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceEnabled",
854 ldapDbusService, ldapConfigObject, ldapEnableInterface,
855 "Enabled", serviceEnabled);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700856}
857
Ed Tanousc1019822024-03-06 12:54:38 -0800858struct AuthMethods
Ed Tanous6c51eab2021-06-03 12:30:29 -0700859{
860 std::optional<bool> basicAuth;
861 std::optional<bool> cookie;
862 std::optional<bool> sessionToken;
863 std::optional<bool> xToken;
864 std::optional<bool> tls;
Ed Tanousc1019822024-03-06 12:54:38 -0800865};
Ed Tanous6c51eab2021-06-03 12:30:29 -0700866
Patrick Williams504af5a2025-02-03 14:29:03 -0500867inline void handleAuthMethodsPatch(
868 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
869 const AuthMethods& auth)
Ed Tanousc1019822024-03-06 12:54:38 -0800870{
871 persistent_data::AuthConfigMethods& authMethodsConfig =
Ed Tanous6c51eab2021-06-03 12:30:29 -0700872 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
873
Ed Tanousc1019822024-03-06 12:54:38 -0800874 if (auth.basicAuth)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700875 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700876 if constexpr (!BMCWEB_BASIC_AUTH)
877 {
878 messages::actionNotSupported(
879 asyncResp->res,
880 "Setting BasicAuth when basic-auth feature is disabled");
881 return;
882 }
883
Ed Tanousc1019822024-03-06 12:54:38 -0800884 authMethodsConfig.basic = *auth.basicAuth;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700885 }
886
Ed Tanousc1019822024-03-06 12:54:38 -0800887 if (auth.cookie)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700888 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700889 if constexpr (!BMCWEB_COOKIE_AUTH)
890 {
891 messages::actionNotSupported(
892 asyncResp->res,
893 "Setting Cookie when cookie-auth feature is disabled");
894 return;
895 }
Ed Tanousc1019822024-03-06 12:54:38 -0800896 authMethodsConfig.cookie = *auth.cookie;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700897 }
898
Ed Tanousc1019822024-03-06 12:54:38 -0800899 if (auth.sessionToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700900 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700901 if constexpr (!BMCWEB_SESSION_AUTH)
902 {
903 messages::actionNotSupported(
904 asyncResp->res,
905 "Setting SessionToken when session-auth feature is disabled");
906 return;
907 }
Ed Tanousc1019822024-03-06 12:54:38 -0800908 authMethodsConfig.sessionToken = *auth.sessionToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700909 }
910
Ed Tanousc1019822024-03-06 12:54:38 -0800911 if (auth.xToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700912 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700913 if constexpr (!BMCWEB_XTOKEN_AUTH)
914 {
915 messages::actionNotSupported(
916 asyncResp->res,
917 "Setting XToken when xtoken-auth feature is disabled");
918 return;
919 }
Ed Tanousc1019822024-03-06 12:54:38 -0800920 authMethodsConfig.xtoken = *auth.xToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700921 }
922
Ed Tanousc1019822024-03-06 12:54:38 -0800923 if (auth.tls)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700924 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700925 if constexpr (!BMCWEB_MUTUAL_TLS_AUTH)
926 {
927 messages::actionNotSupported(
928 asyncResp->res,
929 "Setting TLS when mutual-tls-auth feature is disabled");
930 return;
931 }
Ed Tanousc1019822024-03-06 12:54:38 -0800932 authMethodsConfig.tls = *auth.tls;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700933 }
934
935 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
936 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
937 !authMethodsConfig.tls)
938 {
939 // Do not allow user to disable everything
940 messages::actionNotSupported(asyncResp->res,
941 "of disabling all available methods");
942 return;
943 }
944
945 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
946 authMethodsConfig);
947 // Save configuration immediately
948 persistent_data::getConfig().writeData();
949
950 messages::success(asyncResp->res);
951}
952
953/**
954 * @brief Get the required values from the given JSON, validates the
955 * value and create the LDAP config object.
956 * @param input JSON data
957 * @param asyncResp pointer to the JSON response
958 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
959 */
960
Ed Tanous10cb44f2024-04-11 13:05:20 -0700961struct LdapPatchParams
962{
963 std::optional<std::string> authType;
964 std::optional<std::vector<std::string>> serviceAddressList;
965 std::optional<bool> serviceEnabled;
966 std::optional<std::vector<std::string>> baseDNList;
967 std::optional<std::string> userNameAttribute;
968 std::optional<std::string> groupsAttribute;
969 std::optional<std::string> userName;
970 std::optional<std::string> password;
971 std::optional<
972 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>
973 remoteRoleMapData;
974};
975
976inline void handleLDAPPatch(LdapPatchParams&& input,
Ed Tanous6c51eab2021-06-03 12:30:29 -0700977 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
978 const std::string& serverType)
979{
980 std::string dbusObjectPath;
981 if (serverType == "ActiveDirectory")
982 {
983 dbusObjectPath = adConfigObject;
984 }
985 else if (serverType == "LDAP")
986 {
987 dbusObjectPath = ldapConfigObjectName;
988 }
989 else
990 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700991 BMCWEB_LOG_ERROR("serverType wasn't AD or LDAP but was {}????",
992 serverType);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700993 return;
994 }
995
Ed Tanous10cb44f2024-04-11 13:05:20 -0700996 if (input.authType && *input.authType != "UsernameAndPassword")
Ed Tanous6c51eab2021-06-03 12:30:29 -0700997 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700998 messages::propertyValueNotInList(asyncResp->res, *input.authType,
Ed Tanousc1019822024-03-06 12:54:38 -0800999 "AuthenticationType");
1000 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001001 }
Ed Tanousc1019822024-03-06 12:54:38 -08001002
Ed Tanous10cb44f2024-04-11 13:05:20 -07001003 if (input.serviceAddressList)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001004 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001005 if (input.serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301006 {
Ed Tanouse2616cc2022-06-27 12:45:55 -07001007 messages::propertyValueNotInList(
Ed Tanous10cb44f2024-04-11 13:05:20 -07001008 asyncResp->res, *input.serviceAddressList, "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001009 return;
1010 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001011 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001012 if (input.baseDNList)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001013 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001014 if (input.baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +05301015 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001016 messages::propertyValueNotInList(asyncResp->res, *input.baseDNList,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001017 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301018 return;
1019 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001020 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301021
Ed Tanous6c51eab2021-06-03 12:30:29 -07001022 // nothing to update, then return
Ed Tanous10cb44f2024-04-11 13:05:20 -07001023 if (!input.userName && !input.password && !input.serviceAddressList &&
1024 !input.baseDNList && !input.userNameAttribute &&
1025 !input.groupsAttribute && !input.serviceEnabled &&
1026 !input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001027 {
1028 return;
1029 }
1030
1031 // Get the existing resource first then keep modifying
1032 // whenever any property gets updated.
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001033 getLDAPConfigData(serverType, [asyncResp, input = std::move(input),
1034 dbusObjectPath = std::move(dbusObjectPath)](
1035 bool success,
1036 const LDAPConfigData& confData,
1037 const std::string& serverT) mutable {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001038 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301039 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001040 messages::internalError(asyncResp->res);
1041 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301042 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001043 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1044 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301045 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001046 // Disable the service first and update the rest of
1047 // the properties.
1048 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301049 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001050
Ed Tanous10cb44f2024-04-11 13:05:20 -07001051 if (input.serviceAddressList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301052 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001053 handleServiceAddressPatch(*input.serviceAddressList, asyncResp,
1054 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301055 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001056 if (input.userName)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001057 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001058 handleUserNamePatch(*input.userName, asyncResp, serverT,
1059 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001060 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001061 if (input.password)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001062 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001063 handlePasswordPatch(*input.password, asyncResp, serverT,
1064 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001065 }
1066
Ed Tanous10cb44f2024-04-11 13:05:20 -07001067 if (input.baseDNList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301068 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001069 handleBaseDNPatch(*input.baseDNList, asyncResp, serverT,
1070 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001071 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001072 if (input.userNameAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001073 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001074 handleUserNameAttrPatch(*input.userNameAttribute, asyncResp,
1075 serverT, dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001076 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001077 if (input.groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001078 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001079 handleGroupNameAttrPatch(*input.groupsAttribute, asyncResp, serverT,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001080 dbusObjectPath);
1081 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001082 if (input.serviceEnabled)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001083 {
1084 // if user has given the value as true then enable
1085 // the service. if user has given false then no-op
1086 // as service is already stopped.
Ed Tanous10cb44f2024-04-11 13:05:20 -07001087 if (*input.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301088 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001089 handleServiceEnablePatch(*input.serviceEnabled, asyncResp,
1090 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301091 }
1092 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001093 else
1094 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001095 // if user has not given the service enabled value
1096 // then revert it to the same state as it was
1097 // before.
1098 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1099 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001100 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001101
Ed Tanous10cb44f2024-04-11 13:05:20 -07001102 if (input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001103 {
1104 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001105 *input.remoteRoleMapData);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001106 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001107 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001108}
1109
Ed Tanous492ec932024-12-09 15:42:19 -08001110struct UserUpdateParams
1111{
1112 std::string username;
1113 std::optional<std::string> password;
1114 std::optional<bool> enabled;
1115 std::optional<std::string> roleId;
1116 std::optional<bool> locked;
1117 std::optional<std::vector<std::string>> accountTypes;
1118 bool userSelf;
1119 std::shared_ptr<persistent_data::UserSession> session;
1120 std::string dbusObjectPath;
1121};
1122
Patrick Williams504af5a2025-02-03 14:29:03 -05001123inline void afterVerifyUserExists(
1124 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1125 const UserUpdateParams& params, int rc)
Ed Tanous492ec932024-12-09 15:42:19 -08001126{
1127 if (rc <= 0)
1128 {
1129 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1130 params.username);
1131 return;
1132 }
1133
1134 if (params.password)
1135 {
1136 int retval = pamUpdatePassword(params.username, *params.password);
1137
1138 if (retval == PAM_USER_UNKNOWN)
1139 {
1140 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1141 params.username);
1142 }
1143 else if (retval == PAM_AUTHTOK_ERR)
1144 {
1145 // If password is invalid
1146 messages::propertyValueFormatError(asyncResp->res, nullptr,
1147 "Password");
1148 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
1149 }
1150 else if (retval != PAM_SUCCESS)
1151 {
1152 messages::internalError(asyncResp->res);
1153 return;
1154 }
1155 else
1156 {
1157 // Remove existing sessions of the user when password
1158 // changed
1159 persistent_data::SessionStore::getInstance()
1160 .removeSessionsByUsernameExceptSession(params.username,
1161 params.session);
1162 messages::success(asyncResp->res);
1163 }
1164 }
1165
1166 if (params.enabled)
1167 {
1168 setDbusProperty(
1169 asyncResp, "Enabled", "xyz.openbmc_project.User.Manager",
1170 params.dbusObjectPath, "xyz.openbmc_project.User.Attributes",
1171 "UserEnabled", *params.enabled);
1172 }
1173
1174 if (params.roleId)
1175 {
1176 std::string priv = getPrivilegeFromRoleId(*params.roleId);
1177 if (priv.empty())
1178 {
1179 messages::propertyValueNotInList(asyncResp->res, true, "Locked");
1180 return;
1181 }
1182 setDbusProperty(asyncResp, "RoleId", "xyz.openbmc_project.User.Manager",
1183 params.dbusObjectPath,
1184 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1185 priv);
1186 }
1187
1188 if (params.locked)
1189 {
1190 // admin can unlock the account which is locked by
1191 // successive authentication failures but admin should
1192 // not be allowed to lock an account.
1193 if (*params.locked)
1194 {
1195 messages::propertyValueNotInList(asyncResp->res, "true", "Locked");
1196 return;
1197 }
1198 setDbusProperty(asyncResp, "Locked", "xyz.openbmc_project.User.Manager",
1199 params.dbusObjectPath,
1200 "xyz.openbmc_project.User.Attributes",
1201 "UserLockedForFailedAttempt", *params.locked);
1202 }
1203
1204 if (params.accountTypes)
1205 {
1206 patchAccountTypes(*params.accountTypes, asyncResp,
1207 params.dbusObjectPath, params.userSelf);
1208 }
1209}
1210
Abhishek Patel58345852022-02-02 08:54:25 -06001211inline void updateUserProperties(
Ed Tanous492ec932024-12-09 15:42:19 -08001212 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1213 const std::string& username, const std::optional<std::string>& password,
Abhishek Patel58345852022-02-02 08:54:25 -06001214 const std::optional<bool>& enabled,
1215 const std::optional<std::string>& roleId, const std::optional<bool>& locked,
Ed Tanous492ec932024-12-09 15:42:19 -08001216 const std::optional<std::vector<std::string>>& accountTypes, bool userSelf,
Ravi Tejae518ef32024-05-16 10:33:08 -05001217 const std::shared_ptr<persistent_data::UserSession>& session)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001218{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301219 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1220 tempObjPath /= username;
1221 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001222
Ed Tanous492ec932024-12-09 15:42:19 -08001223 UserUpdateParams params{username, password, enabled,
1224 roleId, locked, accountTypes,
1225 userSelf, session, dbusObjectPath};
1226
Ed Tanous6c51eab2021-06-03 12:30:29 -07001227 dbus::utility::checkDbusPathExists(
Ravi Tejae518ef32024-05-16 10:33:08 -05001228 dbusObjectPath,
Ed Tanous492ec932024-12-09 15:42:19 -08001229 std::bind_front(afterVerifyUserExists, asyncResp, std::move(params)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001230}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001231
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001232inline void handleAccountServiceHead(
1233 App& app, const crow::Request& req,
1234 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001235{
1236 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1237 {
1238 return;
1239 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001240 asyncResp->res.addHeader(
1241 boost::beast::http::field::link,
1242 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1243}
1244
Patrick Williams504af5a2025-02-03 14:29:03 -05001245inline void getClientCertificates(
1246 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1247 const nlohmann::json::json_pointer& keyLocation)
Ed Tanous1aa375b2024-04-13 11:51:10 -07001248{
1249 boost::urls::url url(
1250 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates");
1251 std::array<std::string_view, 1> interfaces = {
1252 "xyz.openbmc_project.Certs.Certificate"};
1253 std::string path = "/xyz/openbmc_project/certs/authority/truststore";
1254
1255 collection_util::getCollectionToKey(asyncResp, url, interfaces, path,
1256 keyLocation);
1257}
1258
1259inline void handleAccountServiceClientCertificatesInstanceHead(
1260 App& app, const crow::Request& req,
1261 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1262 const std::string& /*id*/)
1263{
1264 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1265 {
1266 return;
1267 }
1268
1269 asyncResp->res.addHeader(
1270 boost::beast::http::field::link,
1271 "</redfish/v1/JsonSchemas/Certificate/Certificate.json>; rel=describedby");
1272}
1273
1274inline void handleAccountServiceClientCertificatesInstanceGet(
1275 App& app, const crow::Request& req,
1276 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1277{
1278 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1279 {
1280 return;
1281 }
1282 BMCWEB_LOG_DEBUG("ClientCertificate Certificate ID={}", id);
1283 const boost::urls::url certURL = boost::urls::format(
1284 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/{}",
1285 id);
1286 std::string objPath =
1287 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1288 getCertificateProperties(
1289 asyncResp, objPath,
1290 "xyz.openbmc_project.Certs.Manager.Authority.Truststore", id, certURL,
1291 "Client Certificate");
1292}
1293
1294inline void handleAccountServiceClientCertificatesHead(
1295 App& app, const crow::Request& req,
1296 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1297{
1298 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1299 {
1300 return;
1301 }
1302
1303 asyncResp->res.addHeader(
1304 boost::beast::http::field::link,
1305 "</redfish/v1/JsonSchemas/CertificateCollection/CertificateCollection.json>; rel=describedby");
1306}
1307
1308inline void handleAccountServiceClientCertificatesGet(
1309 App& app, const crow::Request& req,
1310 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1311{
1312 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1313 {
1314 return;
1315 }
Myung Bae0f09ed32025-03-31 15:32:24 -05001316
1317 nlohmann::json& json = asyncResp->res.jsonValue;
1318 json["@odata.id"] =
1319 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates";
1320 json["@odata.type"] = "#CertificateCollection.CertificateCollection";
1321 json["Name"] = "Certificates Collection";
1322 json["Description"] = "Multi-factor Authentication Client Certificates";
Ed Tanous1aa375b2024-04-13 11:51:10 -07001323 getClientCertificates(asyncResp, "/Members"_json_pointer);
1324}
1325
Ed Tanous3ce36882024-06-09 10:58:16 -07001326using account_service::CertificateMappingAttribute;
1327using persistent_data::MTLSCommonNameParseMode;
Patrick Williams504af5a2025-02-03 14:29:03 -05001328inline CertificateMappingAttribute getCertificateMapping(
1329 MTLSCommonNameParseMode parse)
Ed Tanous3ce36882024-06-09 10:58:16 -07001330{
1331 switch (parse)
1332 {
1333 case MTLSCommonNameParseMode::CommonName:
1334 {
1335 return CertificateMappingAttribute::CommonName;
1336 }
Ed Tanous3ce36882024-06-09 10:58:16 -07001337 case MTLSCommonNameParseMode::Whole:
1338 {
1339 return CertificateMappingAttribute::Whole;
1340 }
Ed Tanous3ce36882024-06-09 10:58:16 -07001341 case MTLSCommonNameParseMode::UserPrincipalName:
1342 {
1343 return CertificateMappingAttribute::UserPrincipalName;
1344 }
Ed Tanous3ce36882024-06-09 10:58:16 -07001345 default:
1346 {
1347 return CertificateMappingAttribute::Invalid;
1348 }
Ed Tanous3ce36882024-06-09 10:58:16 -07001349 }
1350}
1351
Patrick Williams504af5a2025-02-03 14:29:03 -05001352inline void handleAccountServiceGet(
1353 App& app, const crow::Request& req,
1354 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001355{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001356 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1357 {
1358 return;
1359 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001360
1361 if (req.session == nullptr)
1362 {
1363 messages::internalError(asyncResp->res);
1364 return;
1365 }
1366
Ed Tanousc1019822024-03-06 12:54:38 -08001367 const persistent_data::AuthConfigMethods& authMethodsConfig =
1368 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1369
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001370 asyncResp->res.addHeader(
1371 boost::beast::http::field::link,
1372 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1373
Ed Tanous1ef4c342022-05-12 16:12:36 -07001374 nlohmann::json& json = asyncResp->res.jsonValue;
1375 json["@odata.id"] = "/redfish/v1/AccountService";
Ravi Teja482a69e2024-04-22 06:56:13 -05001376 json["@odata.type"] = "#AccountService.v1_15_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001377 json["Id"] = "AccountService";
1378 json["Name"] = "Account Service";
1379 json["Description"] = "Account Service";
1380 json["ServiceEnabled"] = true;
1381 json["MaxPasswordLength"] = 20;
1382 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1383 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
Ravi Teja482a69e2024-04-22 06:56:13 -05001384 json["HTTPBasicAuth"] = authMethodsConfig.basic
1385 ? account_service::BasicAuthState::Enabled
1386 : account_service::BasicAuthState::Disabled;
1387
1388 nlohmann::json::array_t allowed;
1389 allowed.emplace_back(account_service::BasicAuthState::Enabled);
1390 allowed.emplace_back(account_service::BasicAuthState::Disabled);
1391 json["HTTPBasicAuth@AllowableValues"] = std::move(allowed);
1392
Ed Tanous1aa375b2024-04-13 11:51:10 -07001393 nlohmann::json::object_t clientCertificate;
1394 clientCertificate["Enabled"] = authMethodsConfig.tls;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001395 clientCertificate["RespondToUnauthenticatedClients"] =
1396 !authMethodsConfig.tlsStrict;
Ed Tanous3ce36882024-06-09 10:58:16 -07001397
1398 using account_service::CertificateMappingAttribute;
1399
1400 CertificateMappingAttribute mapping =
1401 getCertificateMapping(authMethodsConfig.mTLSCommonNameParsingMode);
1402 if (mapping == CertificateMappingAttribute::Invalid)
1403 {
1404 messages::internalError(asyncResp->res);
1405 }
1406 else
1407 {
1408 clientCertificate["CertificateMappingAttribute"] = mapping;
1409 }
Ed Tanous1aa375b2024-04-13 11:51:10 -07001410 nlohmann::json::object_t certificates;
1411 certificates["@odata.id"] =
1412 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates";
1413 certificates["@odata.type"] =
1414 "#CertificateCollection.CertificateCollection";
1415 clientCertificate["Certificates"] = std::move(certificates);
1416 json["MultiFactorAuth"]["ClientCertificate"] = std::move(clientCertificate);
1417
1418 getClientCertificates(
1419 asyncResp,
1420 "/MultiFactorAuth/ClientCertificate/Certificates/Members"_json_pointer);
1421
Ed Tanous1ef4c342022-05-12 16:12:36 -07001422 json["Oem"]["OpenBMC"]["@odata.type"] =
Ed Tanous5b5574a2022-09-26 19:53:36 -07001423 "#OpenBMCAccountService.v1_0_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001424 json["Oem"]["OpenBMC"]["@odata.id"] =
1425 "/redfish/v1/AccountService#/Oem/OpenBMC";
1426 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1427 authMethodsConfig.basic;
1428 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1429 authMethodsConfig.sessionToken;
1430 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1431 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1432 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1433
1434 // /redfish/v1/AccountService/LDAP/Certificates is something only
1435 // ConfigureManager can access then only display when the user has
1436 // permissions ConfigureManager
1437 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001438 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001439
1440 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1441 effectiveUserPrivileges))
1442 {
1443 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1444 "/redfish/v1/AccountService/LDAP/Certificates";
1445 }
Ed Tanousdeae6a72024-11-11 21:58:57 -08001446 dbus::utility::getAllProperties(
1447 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1448 "xyz.openbmc_project.User.AccountPolicy",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001449 [asyncResp](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001450 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001451 if (ec)
1452 {
1453 messages::internalError(asyncResp->res);
1454 return;
1455 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001456
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001457 BMCWEB_LOG_DEBUG("Got {} properties for AccountService",
1458 propertiesList.size());
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001459
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001460 const uint8_t* minPasswordLength = nullptr;
1461 const uint32_t* accountUnlockTimeout = nullptr;
1462 const uint16_t* maxLoginAttemptBeforeLockout = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001463
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001464 const bool success = sdbusplus::unpackPropertiesNoThrow(
1465 dbus_utils::UnpackErrorPrinter(), propertiesList,
1466 "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout",
1467 accountUnlockTimeout, "MaxLoginAttemptBeforeLockout",
1468 maxLoginAttemptBeforeLockout);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001469
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001470 if (!success)
1471 {
1472 messages::internalError(asyncResp->res);
1473 return;
1474 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001475
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001476 if (minPasswordLength != nullptr)
1477 {
1478 asyncResp->res.jsonValue["MinPasswordLength"] =
1479 *minPasswordLength;
1480 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001481
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001482 if (accountUnlockTimeout != nullptr)
1483 {
1484 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1485 *accountUnlockTimeout;
1486 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001487
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001488 if (maxLoginAttemptBeforeLockout != nullptr)
1489 {
1490 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1491 *maxLoginAttemptBeforeLockout;
1492 }
1493 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001494
Ed Tanous02cad962022-06-30 16:50:15 -07001495 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001496 const std::string& ldapType) {
1497 if (!success)
1498 {
1499 return;
1500 }
1501 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1502 };
1503
1504 getLDAPConfigData("LDAP", callback);
1505 getLDAPConfigData("ActiveDirectory", callback);
1506}
1507
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001508inline void handleCertificateMappingAttributePatch(
1509 crow::Response& res, const std::string& certMapAttribute)
Ed Tanous3ce36882024-06-09 10:58:16 -07001510{
1511 MTLSCommonNameParseMode parseMode =
1512 persistent_data::getMTLSCommonNameParseMode(certMapAttribute);
1513 if (parseMode == MTLSCommonNameParseMode::Invalid)
1514 {
1515 messages::propertyValueNotInList(res, "CertificateMappingAttribute",
1516 certMapAttribute);
1517 return;
1518 }
1519
1520 persistent_data::AuthConfigMethods& authMethodsConfig =
1521 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1522 authMethodsConfig.mTLSCommonNameParsingMode = parseMode;
1523}
1524
Ed Tanous3281bcf2024-06-25 16:02:05 -07001525inline void handleRespondToUnauthenticatedClientsPatch(
1526 App& app, const crow::Request& req, crow::Response& res,
1527 bool respondToUnauthenticatedClients)
1528{
1529 if (req.session != nullptr)
1530 {
1531 // Sanity check. If the user isn't currently authenticated with mutual
1532 // TLS, they very likely are about to permanently lock themselves out.
1533 // Make sure they're using mutual TLS before allowing locking.
1534 if (req.session->sessionType != persistent_data::SessionType::MutualTLS)
1535 {
1536 messages::propertyValueExternalConflict(
1537 res,
1538 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
1539 respondToUnauthenticatedClients);
1540 return;
1541 }
1542 }
1543
1544 persistent_data::AuthConfigMethods& authMethodsConfig =
1545 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1546
1547 // Change the settings
1548 authMethodsConfig.tlsStrict = !respondToUnauthenticatedClients;
1549
1550 // Write settings to disk
1551 persistent_data::getConfig().writeData();
1552
1553 // Trigger a reload, to apply the new settings to new connections
1554 app.loadCertificate();
1555}
1556
Ed Tanous1ef4c342022-05-12 16:12:36 -07001557inline void handleAccountServicePatch(
1558 App& app, const crow::Request& req,
1559 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1560{
1561 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1562 {
1563 return;
1564 }
1565 std::optional<uint32_t> unlockTimeout;
1566 std::optional<uint16_t> lockoutThreshold;
1567 std::optional<uint8_t> minPasswordLength;
1568 std::optional<uint16_t> maxPasswordLength;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001569 LdapPatchParams ldapObject;
Ed Tanous3ce36882024-06-09 10:58:16 -07001570 std::optional<std::string> certificateMappingAttribute;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001571 std::optional<bool> respondToUnauthenticatedClients;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001572 LdapPatchParams activeDirectoryObject;
Ed Tanousc1019822024-03-06 12:54:38 -08001573 AuthMethods auth;
Ravi Teja482a69e2024-04-22 06:56:13 -05001574 std::optional<std::string> httpBasicAuth;
Ed Tanous3ce36882024-06-09 10:58:16 -07001575
Patrick Williams504af5a2025-02-03 14:29:03 -05001576 if (!json_util::readJsonPatch( //
1577 req, asyncResp->res, //
1578 "AccountLockoutDuration", unlockTimeout, //
1579 "AccountLockoutThreshold", lockoutThreshold, //
Myung Baeafc474a2024-10-09 00:53:29 -07001580 "ActiveDirectory/Authentication/AuthenticationType",
Patrick Williams504af5a2025-02-03 14:29:03 -05001581 activeDirectoryObject.authType, //
Myung Baeafc474a2024-10-09 00:53:29 -07001582 "ActiveDirectory/Authentication/Password",
Patrick Williams504af5a2025-02-03 14:29:03 -05001583 activeDirectoryObject.password, //
Myung Baeafc474a2024-10-09 00:53:29 -07001584 "ActiveDirectory/Authentication/Username",
Patrick Williams504af5a2025-02-03 14:29:03 -05001585 activeDirectoryObject.userName, //
Myung Baeafc474a2024-10-09 00:53:29 -07001586 "ActiveDirectory/LDAPService/SearchSettings/BaseDistinguishedNames",
Patrick Williams504af5a2025-02-03 14:29:03 -05001587 activeDirectoryObject.baseDNList, //
Myung Baeafc474a2024-10-09 00:53:29 -07001588 "ActiveDirectory/LDAPService/SearchSettings/GroupsAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001589 activeDirectoryObject.groupsAttribute, //
Myung Baeafc474a2024-10-09 00:53:29 -07001590 "ActiveDirectory/LDAPService/SearchSettings/UsernameAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001591 activeDirectoryObject.userNameAttribute, //
Myung Baeafc474a2024-10-09 00:53:29 -07001592 "ActiveDirectory/RemoteRoleMapping",
Patrick Williams504af5a2025-02-03 14:29:03 -05001593 activeDirectoryObject.remoteRoleMapData, //
Myung Baeafc474a2024-10-09 00:53:29 -07001594 "ActiveDirectory/ServiceAddresses",
Patrick Williams504af5a2025-02-03 14:29:03 -05001595 activeDirectoryObject.serviceAddressList, //
Myung Baeafc474a2024-10-09 00:53:29 -07001596 "ActiveDirectory/ServiceEnabled",
Patrick Williams504af5a2025-02-03 14:29:03 -05001597 activeDirectoryObject.serviceEnabled, //
1598 "HTTPBasicAuth", httpBasicAuth, //
Myung Baeafc474a2024-10-09 00:53:29 -07001599 "LDAP/Authentication/AuthenticationType", ldapObject.authType, //
Patrick Williams504af5a2025-02-03 14:29:03 -05001600 "LDAP/Authentication/Password", ldapObject.password, //
1601 "LDAP/Authentication/Username", ldapObject.userName, //
Myung Baeafc474a2024-10-09 00:53:29 -07001602 "LDAP/LDAPService/SearchSettings/BaseDistinguishedNames",
Patrick Williams504af5a2025-02-03 14:29:03 -05001603 ldapObject.baseDNList, //
Myung Baeafc474a2024-10-09 00:53:29 -07001604 "LDAP/LDAPService/SearchSettings/GroupsAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001605 ldapObject.groupsAttribute, //
Myung Baeafc474a2024-10-09 00:53:29 -07001606 "LDAP/LDAPService/SearchSettings/UsernameAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001607 ldapObject.userNameAttribute, //
1608 "LDAP/RemoteRoleMapping", ldapObject.remoteRoleMapData, //
1609 "LDAP/ServiceAddresses", ldapObject.serviceAddressList, //
1610 "LDAP/ServiceEnabled", ldapObject.serviceEnabled, //
1611 "MaxPasswordLength", maxPasswordLength, //
1612 "MinPasswordLength", minPasswordLength, //
Myung Baeafc474a2024-10-09 00:53:29 -07001613 "MultiFactorAuth/ClientCertificate/CertificateMappingAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001614 certificateMappingAttribute, //
Myung Baeafc474a2024-10-09 00:53:29 -07001615 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
Patrick Williams504af5a2025-02-03 14:29:03 -05001616 respondToUnauthenticatedClients, //
1617 "Oem/OpenBMC/AuthMethods/BasicAuth", auth.basicAuth, //
1618 "Oem/OpenBMC/AuthMethods/Cookie", auth.cookie, //
1619 "Oem/OpenBMC/AuthMethods/SessionToken", auth.sessionToken, //
1620 "Oem/OpenBMC/AuthMethods/TLS", auth.tls, //
1621 "Oem/OpenBMC/AuthMethods/XToken", auth.xToken //
Myung Baeafc474a2024-10-09 00:53:29 -07001622 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001623 {
1624 return;
1625 }
1626
Ravi Teja482a69e2024-04-22 06:56:13 -05001627 if (httpBasicAuth)
1628 {
1629 if (*httpBasicAuth == "Enabled")
1630 {
1631 auth.basicAuth = true;
1632 }
1633 else if (*httpBasicAuth == "Disabled")
1634 {
1635 auth.basicAuth = false;
1636 }
1637 else
1638 {
1639 messages::propertyValueNotInList(asyncResp->res, "HttpBasicAuth",
1640 *httpBasicAuth);
1641 }
1642 }
1643
Ed Tanous3281bcf2024-06-25 16:02:05 -07001644 if (respondToUnauthenticatedClients)
1645 {
1646 handleRespondToUnauthenticatedClientsPatch(
1647 app, req, asyncResp->res, *respondToUnauthenticatedClients);
1648 }
1649
Ed Tanous3ce36882024-06-09 10:58:16 -07001650 if (certificateMappingAttribute)
1651 {
1652 handleCertificateMappingAttributePatch(asyncResp->res,
1653 *certificateMappingAttribute);
1654 }
1655
Ed Tanous1ef4c342022-05-12 16:12:36 -07001656 if (minPasswordLength)
1657 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001658 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301659 asyncResp, "MinPasswordLength", "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001660 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001661 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
Ginu Georgee93abac2024-06-14 17:35:27 +05301662 *minPasswordLength);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001663 }
1664
1665 if (maxPasswordLength)
1666 {
1667 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1668 }
1669
Ed Tanous10cb44f2024-04-11 13:05:20 -07001670 handleLDAPPatch(std::move(activeDirectoryObject), asyncResp,
1671 "ActiveDirectory");
1672 handleLDAPPatch(std::move(ldapObject), asyncResp, "LDAP");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001673
Ed Tanousc1019822024-03-06 12:54:38 -08001674 handleAuthMethodsPatch(asyncResp, auth);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001675
Ed Tanous1ef4c342022-05-12 16:12:36 -07001676 if (unlockTimeout)
1677 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001678 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301679 asyncResp, "AccountLockoutDuration",
1680 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001681 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
Ed Tanous1ef4c342022-05-12 16:12:36 -07001682 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
Ginu Georgee93abac2024-06-14 17:35:27 +05301683 *unlockTimeout);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001684 }
1685 if (lockoutThreshold)
1686 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001687 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301688 asyncResp, "AccountLockoutThreshold",
1689 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001690 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001691 "xyz.openbmc_project.User.AccountPolicy",
Ginu Georgee93abac2024-06-14 17:35:27 +05301692 "MaxLoginAttemptBeforeLockout", *lockoutThreshold);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001693 }
1694}
1695
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001696inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001697 App& app, const crow::Request& req,
1698 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1699{
1700 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1701 {
1702 return;
1703 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001704 asyncResp->res.addHeader(
1705 boost::beast::http::field::link,
1706 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1707}
1708
1709inline void handleAccountCollectionGet(
1710 App& app, const crow::Request& req,
1711 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1712{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001713 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1714 {
1715 return;
1716 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001717
1718 if (req.session == nullptr)
1719 {
1720 messages::internalError(asyncResp->res);
1721 return;
1722 }
1723
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001724 asyncResp->res.addHeader(
1725 boost::beast::http::field::link,
1726 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001727
1728 asyncResp->res.jsonValue["@odata.id"] =
1729 "/redfish/v1/AccountService/Accounts";
1730 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1731 "ManagerAccountCollection";
1732 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1733 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1734
1735 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001736 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001737
1738 std::string thisUser;
1739 if (req.session)
1740 {
1741 thisUser = req.session->username;
1742 }
George Liu5eb468d2023-06-20 17:03:24 +08001743 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
1744 dbus::utility::getManagedObjects(
1745 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001746 [asyncResp, thisUser, effectiveUserPrivileges](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001747 const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001748 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001749 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001750 {
1751 messages::internalError(asyncResp->res);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001752 return;
1753 }
1754
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001755 bool userCanSeeAllAccounts =
1756 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1757
1758 bool userCanSeeSelf =
1759 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1760
1761 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1762 memberArray = nlohmann::json::array();
1763
1764 for (const auto& userpath : users)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001765 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001766 std::string user = userpath.first.filename();
1767 if (user.empty())
1768 {
1769 messages::internalError(asyncResp->res);
1770 BMCWEB_LOG_ERROR("Invalid firmware ID");
1771
1772 return;
1773 }
1774
1775 // As clarified by Redfish here:
1776 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1777 // Users without ConfigureUsers, only see their own
1778 // account. Users with ConfigureUsers, see all
1779 // accounts.
1780 if (userCanSeeAllAccounts ||
1781 (thisUser == user && userCanSeeSelf))
1782 {
1783 nlohmann::json::object_t member;
1784 member["@odata.id"] = boost::urls::format(
1785 "/redfish/v1/AccountService/Accounts/{}", user);
1786 memberArray.emplace_back(std::move(member));
1787 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001788 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001789 asyncResp->res.jsonValue["Members@odata.count"] =
1790 memberArray.size();
1791 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001792}
1793
Ninad Palsule97e90da2023-05-17 14:04:52 -05001794inline void processAfterCreateUser(
1795 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1796 const std::string& username, const std::string& password,
1797 const boost::system::error_code& ec, sdbusplus::message_t& m)
1798{
1799 if (ec)
1800 {
1801 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1802 return;
1803 }
1804
1805 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1806 {
1807 // At this point we have a user that's been
1808 // created, but the password set
1809 // failed.Something is wrong, so delete the user
1810 // that we've already created
1811 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1812 tempObjPath /= username;
1813 const std::string userPath(tempObjPath);
1814
Ed Tanous177612a2025-02-14 15:16:09 -08001815 dbus::utility::async_method_call(
1816 asyncResp,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001817 [asyncResp, password](const boost::system::error_code& ec3) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001818 if (ec3)
1819 {
1820 messages::internalError(asyncResp->res);
1821 return;
1822 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001823
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001824 // If password is invalid
1825 messages::propertyValueFormatError(asyncResp->res, nullptr,
1826 "Password");
1827 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001828 "xyz.openbmc_project.User.Manager", userPath,
1829 "xyz.openbmc_project.Object.Delete", "Delete");
1830
Ed Tanous62598e32023-07-17 17:06:25 -07001831 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
Ninad Palsule97e90da2023-05-17 14:04:52 -05001832 return;
1833 }
1834
1835 messages::created(asyncResp->res);
1836 asyncResp->res.addHeader("Location",
1837 "/redfish/v1/AccountService/Accounts/" + username);
1838}
1839
1840inline void processAfterGetAllGroups(
1841 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1842 const std::string& username, const std::string& password,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001843 const std::string& roleId, bool enabled,
Ninad Palsule9ba73932023-06-01 16:38:57 -05001844 std::optional<std::vector<std::string>> accountTypes,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001845 const std::vector<std::string>& allGroupsList)
Ninad Palsule97e90da2023-05-17 14:04:52 -05001846{
Ninad Palsule3e72c202023-03-27 17:19:55 -05001847 std::vector<std::string> userGroups;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001848 std::vector<std::string> accountTypeUserGroups;
1849
1850 // If user specified account types then convert them to unix user groups
1851 if (accountTypes)
1852 {
1853 if (!getUserGroupFromAccountType(asyncResp->res, *accountTypes,
1854 accountTypeUserGroups))
1855 {
1856 // Problem in mapping Account Types to User Groups, Error already
1857 // logged.
1858 return;
1859 }
1860 }
1861
Ninad Palsule3e72c202023-03-27 17:19:55 -05001862 for (const auto& grp : allGroupsList)
1863 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001864 // If user specified the account type then only accept groups which are
1865 // in the account types group list.
1866 if (!accountTypeUserGroups.empty())
1867 {
1868 bool found = false;
1869 for (const auto& grp1 : accountTypeUserGroups)
1870 {
1871 if (grp == grp1)
1872 {
1873 found = true;
1874 break;
1875 }
1876 }
1877 if (!found)
1878 {
1879 continue;
1880 }
1881 }
1882
Ninad Palsule3e72c202023-03-27 17:19:55 -05001883 // Console access is provided to the user who is a member of
1884 // hostconsole group and has a administrator role. So, set
1885 // hostconsole group only for the administrator.
Ninad Palsule9ba73932023-06-01 16:38:57 -05001886 if ((grp == "hostconsole") && (roleId != "priv-admin"))
Ninad Palsule3e72c202023-03-27 17:19:55 -05001887 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001888 if (!accountTypeUserGroups.empty())
1889 {
Ed Tanous62598e32023-07-17 17:06:25 -07001890 BMCWEB_LOG_ERROR(
1891 "Only administrator can get HostConsole access");
Ninad Palsule9ba73932023-06-01 16:38:57 -05001892 asyncResp->res.result(boost::beast::http::status::bad_request);
1893 return;
1894 }
1895 continue;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001896 }
Ninad Palsule9ba73932023-06-01 16:38:57 -05001897 userGroups.emplace_back(grp);
1898 }
1899
1900 // Make sure user specified groups are valid. This is internal error because
1901 // it some inconsistencies between user manager and bmcweb.
1902 if (!accountTypeUserGroups.empty() &&
1903 accountTypeUserGroups.size() != userGroups.size())
1904 {
1905 messages::internalError(asyncResp->res);
1906 return;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001907 }
Ed Tanous177612a2025-02-14 15:16:09 -08001908 dbus::utility::async_method_call(
1909 asyncResp,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001910 [asyncResp, username, password](const boost::system::error_code& ec2,
1911 sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001912 processAfterCreateUser(asyncResp, username, password, ec2, m);
1913 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001914 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ninad Palsule3e72c202023-03-27 17:19:55 -05001915 "xyz.openbmc_project.User.Manager", "CreateUser", username, userGroups,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001916 roleId, enabled);
Ninad Palsule97e90da2023-05-17 14:04:52 -05001917}
1918
Ed Tanous1ef4c342022-05-12 16:12:36 -07001919inline void handleAccountCollectionPost(
1920 App& app, const crow::Request& req,
1921 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1922{
1923 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1924 {
1925 return;
1926 }
1927 std::string username;
1928 std::string password;
Ed Tanouse01d0c32023-06-30 13:21:32 -07001929 std::optional<std::string> roleIdJson;
1930 std::optional<bool> enabledJson;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001931 std::optional<std::vector<std::string>> accountTypes;
Patrick Williams504af5a2025-02-03 14:29:03 -05001932 if (!json_util::readJsonPatch( //
1933 req, asyncResp->res, //
Myung Baeafc474a2024-10-09 00:53:29 -07001934 "AccountTypes", accountTypes, //
Patrick Williams504af5a2025-02-03 14:29:03 -05001935 "Enabled", enabledJson, //
1936 "Password", password, //
1937 "RoleId", roleIdJson, //
1938 "UserName", username //
Myung Baeafc474a2024-10-09 00:53:29 -07001939 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001940 {
1941 return;
1942 }
1943
Ed Tanouse01d0c32023-06-30 13:21:32 -07001944 std::string roleId = roleIdJson.value_or("User");
1945 std::string priv = getPrivilegeFromRoleId(roleId);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001946 if (priv.empty())
1947 {
Ed Tanouse01d0c32023-06-30 13:21:32 -07001948 messages::propertyValueNotInList(asyncResp->res, roleId, "RoleId");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001949 return;
1950 }
Asmitha Karunanithi239adf82022-03-25 02:59:03 -05001951 roleId = priv;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001952
Ed Tanouse01d0c32023-06-30 13:21:32 -07001953 bool enabled = enabledJson.value_or(true);
1954
Ed Tanous1ef4c342022-05-12 16:12:36 -07001955 // Reading AllGroups property
Ed Tanousdeae6a72024-11-11 21:58:57 -08001956 dbus::utility::getProperty<std::vector<std::string>>(
1957 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1958 "xyz.openbmc_project.User.Manager", "AllGroups",
Ninad Palsule9ba73932023-06-01 16:38:57 -05001959 [asyncResp, username, password{std::move(password)}, roleId, enabled,
1960 accountTypes](const boost::system::error_code& ec,
1961 const std::vector<std::string>& allGroupsList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001962 if (ec)
1963 {
Gunnar Millsa0735a42024-09-06 12:51:11 -05001964 BMCWEB_LOG_ERROR("D-Bus response error {}", ec);
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001965 messages::internalError(asyncResp->res);
1966 return;
1967 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001968
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001969 if (allGroupsList.empty())
1970 {
1971 messages::internalError(asyncResp->res);
1972 return;
1973 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001974
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001975 processAfterGetAllGroups(asyncResp, username, password, roleId,
1976 enabled, accountTypes, allGroupsList);
1977 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001978}
1979
Patrick Williams504af5a2025-02-03 14:29:03 -05001980inline void handleAccountHead(
1981 App& app, const crow::Request& req,
1982 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1983 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001984{
1985 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1986 {
1987 return;
1988 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001989 asyncResp->res.addHeader(
1990 boost::beast::http::field::link,
1991 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1992}
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001993
Patrick Williams504af5a2025-02-03 14:29:03 -05001994inline void handleAccountGet(
1995 App& app, const crow::Request& req,
1996 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1997 const std::string& accountName)
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001998{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001999 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2000 {
2001 return;
2002 }
2003 asyncResp->res.addHeader(
2004 boost::beast::http::field::link,
2005 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
2006
Ed Tanous25b54db2024-04-17 15:40:31 -07002007 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2008 {
2009 // If authentication is disabled, there are no user accounts
2010 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2011 accountName);
2012 return;
2013 }
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08002014
Ed Tanous1ef4c342022-05-12 16:12:36 -07002015 if (req.session == nullptr)
2016 {
2017 messages::internalError(asyncResp->res);
2018 return;
2019 }
2020 if (req.session->username != accountName)
2021 {
2022 // At this point we've determined that the user is trying to
2023 // modify a user that isn't them. We need to verify that they
2024 // have permissions to modify other users, so re-run the auth
2025 // check with the same permissions, minus ConfigureSelf.
2026 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002027 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002028 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
2029 "ConfigureManager"};
2030 if (!effectiveUserPrivileges.isSupersetOf(
2031 requiredPermissionsToChangeNonSelf))
2032 {
Ed Tanous62598e32023-07-17 17:06:25 -07002033 BMCWEB_LOG_DEBUG("GET Account denied access");
Ed Tanous1ef4c342022-05-12 16:12:36 -07002034 messages::insufficientPrivilege(asyncResp->res);
2035 return;
2036 }
2037 }
2038
George Liu5eb468d2023-06-20 17:03:24 +08002039 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
2040 dbus::utility::getManagedObjects(
2041 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07002042 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002043 accountName](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07002044 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002045 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002046 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002047 messages::internalError(asyncResp->res);
2048 return;
2049 }
2050 const auto userIt = std::ranges::find_if(
2051 users,
2052 [accountName](
2053 const std::pair<sdbusplus::message::object_path,
2054 dbus::utility::DBusInterfacesMap>& user) {
2055 return accountName == user.first.filename();
2056 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002057
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002058 if (userIt == users.end())
2059 {
2060 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2061 accountName);
2062 return;
2063 }
2064
2065 asyncResp->res.jsonValue["@odata.type"] =
2066 "#ManagerAccount.v1_7_0.ManagerAccount";
2067 asyncResp->res.jsonValue["Name"] = "User Account";
2068 asyncResp->res.jsonValue["Description"] = "User Account";
2069 asyncResp->res.jsonValue["Password"] = nullptr;
2070 asyncResp->res.jsonValue["StrictAccountTypes"] = true;
2071
2072 for (const auto& interface : userIt->second)
2073 {
2074 if (interface.first == "xyz.openbmc_project.User.Attributes")
2075 {
Asmitha Karunanithib437a532024-12-20 04:54:25 -06002076 const bool* userEnabled = nullptr;
2077 const bool* userLocked = nullptr;
2078 const std::string* userPrivPtr = nullptr;
2079 const bool* userPasswordExpired = nullptr;
2080 const std::vector<std::string>* userGroups = nullptr;
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002081
Asmitha Karunanithib437a532024-12-20 04:54:25 -06002082 const bool success = sdbusplus::unpackPropertiesNoThrow(
2083 dbus_utils::UnpackErrorPrinter(), interface.second,
2084 "UserEnabled", userEnabled,
2085 "UserLockedForFailedAttempt", userLocked,
2086 "UserPrivilege", userPrivPtr, "UserPasswordExpired",
2087 userPasswordExpired, "UserGroups", userGroups);
2088 if (!success)
2089 {
2090 messages::internalError(asyncResp->res);
2091 return;
2092 }
2093 if (userEnabled == nullptr)
2094 {
2095 BMCWEB_LOG_ERROR("UserEnabled wasn't a bool");
2096 messages::internalError(asyncResp->res);
2097 return;
2098 }
2099 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
2100
2101 if (userLocked == nullptr)
2102 {
2103 BMCWEB_LOG_ERROR("UserLockedForF"
2104 "ailedAttempt "
2105 "wasn't a bool");
2106 messages::internalError(asyncResp->res);
2107 return;
2108 }
2109 asyncResp->res.jsonValue["Locked"] = *userLocked;
2110 nlohmann::json::array_t allowed;
2111 // can only unlock accounts
2112 allowed.emplace_back("false");
2113 asyncResp->res.jsonValue["Locked@Redfish.AllowableValues"] =
2114 std::move(allowed);
2115
2116 if (userPrivPtr == nullptr)
2117 {
2118 BMCWEB_LOG_ERROR("UserPrivilege wasn't a "
2119 "string");
2120 messages::internalError(asyncResp->res);
2121 return;
2122 }
2123 std::string role = getRoleIdFromPrivilege(*userPrivPtr);
2124 if (role.empty())
2125 {
2126 BMCWEB_LOG_ERROR("Invalid user role");
2127 messages::internalError(asyncResp->res);
2128 return;
2129 }
2130 asyncResp->res.jsonValue["RoleId"] = role;
2131
2132 nlohmann::json& roleEntry =
2133 asyncResp->res.jsonValue["Links"]["Role"];
2134 roleEntry["@odata.id"] = boost::urls::format(
2135 "/redfish/v1/AccountService/Roles/{}", role);
2136
2137 if (userPasswordExpired == nullptr)
2138 {
2139 BMCWEB_LOG_ERROR("UserPasswordExpired wasn't a bool");
2140 messages::internalError(asyncResp->res);
2141 return;
2142 }
2143 asyncResp->res.jsonValue["PasswordChangeRequired"] =
2144 *userPasswordExpired;
2145
2146 if (userGroups == nullptr)
2147 {
2148 BMCWEB_LOG_ERROR("userGroups wasn't a string vector");
2149 messages::internalError(asyncResp->res);
2150 return;
2151 }
2152 if (!translateUserGroup(*userGroups, asyncResp->res))
2153 {
2154 BMCWEB_LOG_ERROR("userGroups mapping failed");
2155 messages::internalError(asyncResp->res);
2156 return;
Abhishek Patelc7229812022-02-01 10:07:15 -06002157 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002158 }
2159 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002160
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002161 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
2162 "/redfish/v1/AccountService/Accounts/{}", accountName);
2163 asyncResp->res.jsonValue["Id"] = accountName;
2164 asyncResp->res.jsonValue["UserName"] = accountName;
2165 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002166}
2167
Patrick Williams504af5a2025-02-03 14:29:03 -05002168inline void handleAccountDelete(
2169 App& app, const crow::Request& req,
2170 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2171 const std::string& username)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002172{
2173 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2174 {
2175 return;
2176 }
2177
Ed Tanous25b54db2024-04-17 15:40:31 -07002178 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2179 {
2180 // If authentication is disabled, there are no user accounts
2181 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2182 return;
2183 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002184 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
2185 tempObjPath /= username;
2186 const std::string userPath(tempObjPath);
2187
Ed Tanous177612a2025-02-14 15:16:09 -08002188 dbus::utility::async_method_call(
2189 asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002190 [asyncResp, username](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002191 if (ec)
2192 {
2193 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2194 username);
2195 return;
2196 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002197
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002198 messages::accountRemoved(asyncResp->res);
2199 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002200 "xyz.openbmc_project.User.Manager", userPath,
2201 "xyz.openbmc_project.Object.Delete", "Delete");
2202}
2203
Patrick Williams504af5a2025-02-03 14:29:03 -05002204inline void handleAccountPatch(
2205 App& app, const crow::Request& req,
2206 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2207 const std::string& username)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002208{
2209 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2210 {
2211 return;
2212 }
Ed Tanous25b54db2024-04-17 15:40:31 -07002213 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2214 {
2215 // If authentication is disabled, there are no user accounts
2216 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2217 return;
2218 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002219 std::optional<std::string> newUserName;
2220 std::optional<std::string> password;
2221 std::optional<bool> enabled;
2222 std::optional<std::string> roleId;
2223 std::optional<bool> locked;
Abhishek Patel58345852022-02-02 08:54:25 -06002224 std::optional<std::vector<std::string>> accountTypes;
2225
Ed Tanous1ef4c342022-05-12 16:12:36 -07002226 if (req.session == nullptr)
2227 {
2228 messages::internalError(asyncResp->res);
2229 return;
2230 }
2231
Ed Tanous2b9c1df2024-04-06 13:52:01 -07002232 bool userSelf = (username == req.session->username);
2233
Ed Tanous1ef4c342022-05-12 16:12:36 -07002234 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002235 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002236 Privileges configureUsers = {"ConfigureUsers"};
2237 bool userHasConfigureUsers =
2238 effectiveUserPrivileges.isSupersetOf(configureUsers);
2239 if (userHasConfigureUsers)
2240 {
2241 // Users with ConfigureUsers can modify for all users
Patrick Williams504af5a2025-02-03 14:29:03 -05002242 if (!json_util::readJsonPatch( //
2243 req, asyncResp->res, //
Myung Baeafc474a2024-10-09 00:53:29 -07002244 "AccountTypes", accountTypes, //
Patrick Williams504af5a2025-02-03 14:29:03 -05002245 "Enabled", enabled, //
2246 "Locked", locked, //
2247 "Password", password, //
2248 "RoleId", roleId, //
2249 "UserName", newUserName //
Myung Baeafc474a2024-10-09 00:53:29 -07002250 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07002251 {
2252 return;
2253 }
2254 }
2255 else
2256 {
2257 // ConfigureSelf accounts can only modify their own account
Abhishek Patel58345852022-02-02 08:54:25 -06002258 if (!userSelf)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002259 {
2260 messages::insufficientPrivilege(asyncResp->res);
2261 return;
2262 }
2263
2264 // ConfigureSelf accounts can only modify their password
2265 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
2266 password))
2267 {
2268 return;
2269 }
2270 }
2271
2272 // if user name is not provided in the patch method or if it
2273 // matches the user name in the URI, then we are treating it as
2274 // updating user properties other then username. If username
2275 // provided doesn't match the URI, then we are treating this as
2276 // user rename request.
2277 if (!newUserName || (newUserName.value() == username))
2278 {
2279 updateUserProperties(asyncResp, username, password, enabled, roleId,
Ravi Tejae518ef32024-05-16 10:33:08 -05002280 locked, accountTypes, userSelf, req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002281 return;
2282 }
Ed Tanous177612a2025-02-14 15:16:09 -08002283 dbus::utility::async_method_call(
2284 asyncResp,
Ed Tanous1ef4c342022-05-12 16:12:36 -07002285 [asyncResp, username, password(std::move(password)),
2286 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Ed Tanous608ad2c2024-05-20 19:14:50 -07002287 locked, userSelf, session = req.session,
2288 accountTypes(std::move(accountTypes))](
Ed Tanouse81de512023-06-27 17:07:00 -07002289 const boost::system::error_code& ec, sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002290 if (ec)
2291 {
2292 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
2293 username);
2294 return;
2295 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002296
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002297 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
Ed Tanous608ad2c2024-05-20 19:14:50 -07002298 locked, accountTypes, userSelf, session);
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002299 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002300 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
2301 "xyz.openbmc_project.User.Manager", "RenameUser", username,
2302 *newUserName);
2303}
2304
Ed Tanous6c51eab2021-06-03 12:30:29 -07002305inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07002306{
Ed Tanous6c51eab2021-06-03 12:30:29 -07002307 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002308 .privileges(redfish::privileges::headAccountService)
2309 .methods(boost::beast::http::verb::head)(
2310 std::bind_front(handleAccountServiceHead, std::ref(app)));
2311
2312 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07002313 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002314 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002315 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002316
Ed Tanousf5ffd802021-07-19 10:55:33 -07002317 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06002318 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07002319 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002320 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07002321
Ed Tanous1aa375b2024-04-13 11:51:10 -07002322 BMCWEB_ROUTE(
2323 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002324 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002325 .privileges(redfish::privileges::headCertificateCollection)
2326 .methods(boost::beast::http::verb::head)(std::bind_front(
2327 handleAccountServiceClientCertificatesHead, std::ref(app)));
2328
2329 BMCWEB_ROUTE(
2330 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002331 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002332 .privileges(redfish::privileges::getCertificateCollection)
2333 .methods(boost::beast::http::verb::get)(std::bind_front(
2334 handleAccountServiceClientCertificatesGet, std::ref(app)));
2335
2336 BMCWEB_ROUTE(
2337 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002338 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002339 .privileges(redfish::privileges::headCertificate)
2340 .methods(boost::beast::http::verb::head)(std::bind_front(
2341 handleAccountServiceClientCertificatesInstanceHead, std::ref(app)));
2342
2343 BMCWEB_ROUTE(
2344 app,
2345 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
2346 .privileges(redfish::privileges::getCertificate)
2347 .methods(boost::beast::http::verb::get)(std::bind_front(
2348 handleAccountServiceClientCertificatesInstanceGet, std::ref(app)));
2349
Ed Tanous6c51eab2021-06-03 12:30:29 -07002350 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002351 .privileges(redfish::privileges::headManagerAccountCollection)
2352 .methods(boost::beast::http::verb::head)(
2353 std::bind_front(handleAccountCollectionHead, std::ref(app)));
2354
2355 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002356 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002357 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002358 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07002359
Ed Tanous6c51eab2021-06-03 12:30:29 -07002360 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002361 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002362 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002363 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07002364
2365 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002366 .privileges(redfish::privileges::headManagerAccount)
2367 .methods(boost::beast::http::verb::head)(
2368 std::bind_front(handleAccountHead, std::ref(app)));
2369
2370 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07002371 .privileges(redfish::privileges::getManagerAccount)
2372 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002373 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002374
2375 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002376 // TODO this privilege should be using the generated endpoints, but
2377 // because of the special handling of ConfigureSelf, it's not able to
2378 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07002379 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
2380 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002381 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002382
2383 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002384 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002385 .methods(boost::beast::http::verb::delete_)(
Gunnar Mills20fc3072023-01-27 15:13:36 -06002386 std::bind_front(handleAccountDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002387}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002388
Ed Tanous1abe55e2018-09-05 08:30:59 -07002389} // namespace redfish