blob: 173d9665fdadd08703201c7c1a71a6d09aa5f729 [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";
69constexpr const char* ldapCreateInterface =
70 "xyz.openbmc_project.User.Ldap.Create";
71constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053072constexpr const char* ldapPrivMapperInterface =
73 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053074
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060075struct LDAPRoleMapData
76{
77 std::string groupName;
78 std::string privilege;
79};
80
Ratan Gupta6973a582018-12-13 18:25:44 +053081struct LDAPConfigData
82{
Ed Tanous47f29342024-03-19 12:18:06 -070083 std::string uri;
84 std::string bindDN;
85 std::string baseDN;
86 std::string searchScope;
87 std::string serverType;
Ratan Gupta6973a582018-12-13 18:25:44 +053088 bool serviceEnabled = false;
Ed Tanous47f29342024-03-19 12:18:06 -070089 std::string userNameAttribute;
90 std::string groupAttribute;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060091 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053092};
93
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060094inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053095{
96 if (role == "priv-admin")
97 {
98 return "Administrator";
99 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700100 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530101 {
AppaRao Pulic80fee52019-10-16 14:49:36 +0530102 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530103 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700104 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530105 {
106 return "Operator";
107 }
108 return "";
109}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600110inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530111{
112 if (role == "Administrator")
113 {
114 return "priv-admin";
115 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700116 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530117 {
118 return "priv-user";
119 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700120 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530121 {
122 return "priv-operator";
123 }
124 return "";
125}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700126
Abhishek Patelc7229812022-02-01 10:07:15 -0600127/**
128 * @brief Maps user group names retrieved from D-Bus object to
129 * Account Types.
130 *
131 * @param[in] userGroups List of User groups
132 * @param[out] res AccountTypes populated
133 *
134 * @return true in case of success, false if UserGroups contains
135 * invalid group name(s).
136 */
137inline bool translateUserGroup(const std::vector<std::string>& userGroups,
138 crow::Response& res)
139{
140 std::vector<std::string> accountTypes;
141 for (const auto& userGroup : userGroups)
142 {
143 if (userGroup == "redfish")
144 {
145 accountTypes.emplace_back("Redfish");
146 accountTypes.emplace_back("WebUI");
147 }
148 else if (userGroup == "ipmi")
149 {
150 accountTypes.emplace_back("IPMI");
151 }
152 else if (userGroup == "ssh")
153 {
Abhishek Patelc7229812022-02-01 10:07:15 -0600154 accountTypes.emplace_back("ManagerConsole");
155 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500156 else if (userGroup == "hostconsole")
157 {
158 // The hostconsole group controls who can access the host console
159 // port via ssh and websocket.
160 accountTypes.emplace_back("HostConsole");
161 }
Abhishek Patelc7229812022-02-01 10:07:15 -0600162 else if (userGroup == "web")
163 {
164 // 'web' is one of the valid groups in the UserGroups property of
165 // the user account in the D-Bus object. This group is currently not
166 // doing anything, and is considered to be equivalent to 'redfish'.
167 // 'redfish' user group is mapped to 'Redfish'and 'WebUI'
168 // AccountTypes, so do nothing here...
169 }
170 else
171 {
Ed Tanous8ece0e42024-01-02 13:16:50 -0800172 // Invalid user group name. Caller throws an exception.
Abhishek Patelc7229812022-02-01 10:07:15 -0600173 return false;
174 }
175 }
176
177 res.jsonValue["AccountTypes"] = std::move(accountTypes);
178 return true;
179}
180
Abhishek Patel58345852022-02-02 08:54:25 -0600181/**
182 * @brief Builds User Groups from the Account Types
183 *
184 * @param[in] asyncResp Async Response
185 * @param[in] accountTypes List of Account Types
186 * @param[out] userGroups List of User Groups mapped from Account Types
187 *
188 * @return true if Account Types mapped to User Groups, false otherwise.
189 */
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400190inline bool getUserGroupFromAccountType(
191 crow::Response& res, const std::vector<std::string>& accountTypes,
192 std::vector<std::string>& userGroups)
Abhishek Patel58345852022-02-02 08:54:25 -0600193{
194 // Need both Redfish and WebUI Account Types to map to 'redfish' User Group
195 bool redfishType = false;
196 bool webUIType = false;
197
198 for (const auto& accountType : accountTypes)
199 {
200 if (accountType == "Redfish")
201 {
202 redfishType = true;
203 }
204 else if (accountType == "WebUI")
205 {
206 webUIType = true;
207 }
208 else if (accountType == "IPMI")
209 {
210 userGroups.emplace_back("ipmi");
211 }
212 else if (accountType == "HostConsole")
213 {
214 userGroups.emplace_back("hostconsole");
215 }
216 else if (accountType == "ManagerConsole")
217 {
218 userGroups.emplace_back("ssh");
219 }
220 else
221 {
222 // Invalid Account Type
223 messages::propertyValueNotInList(res, "AccountTypes", accountType);
224 return false;
225 }
226 }
227
228 // Both Redfish and WebUI Account Types are needed to PATCH
229 if (redfishType ^ webUIType)
230 {
Ed Tanous62598e32023-07-17 17:06:25 -0700231 BMCWEB_LOG_ERROR(
232 "Missing Redfish or WebUI Account Type to set redfish User Group");
Abhishek Patel58345852022-02-02 08:54:25 -0600233 messages::strictAccountTypes(res, "AccountTypes");
234 return false;
235 }
236
237 if (redfishType && webUIType)
238 {
239 userGroups.emplace_back("redfish");
240 }
241
242 return true;
243}
244
245/**
246 * @brief Sets UserGroups property of the user based on the Account Types
247 *
248 * @param[in] accountTypes List of User Account Types
249 * @param[in] asyncResp Async Response
250 * @param[in] dbusObjectPath D-Bus Object Path
251 * @param[in] userSelf true if User is updating OWN Account Types
252 */
Patrick Williams504af5a2025-02-03 14:29:03 -0500253inline void patchAccountTypes(
254 const std::vector<std::string>& accountTypes,
255 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
256 const std::string& dbusObjectPath, bool userSelf)
Abhishek Patel58345852022-02-02 08:54:25 -0600257{
258 // Check if User is disabling own Redfish Account Type
259 if (userSelf &&
260 (accountTypes.cend() ==
261 std::find(accountTypes.cbegin(), accountTypes.cend(), "Redfish")))
262 {
Ed Tanous62598e32023-07-17 17:06:25 -0700263 BMCWEB_LOG_ERROR(
264 "User disabling OWN Redfish Account Type is not allowed");
Abhishek Patel58345852022-02-02 08:54:25 -0600265 messages::strictAccountTypes(asyncResp->res, "AccountTypes");
266 return;
267 }
268
269 std::vector<std::string> updatedUserGroups;
270 if (!getUserGroupFromAccountType(asyncResp->res, accountTypes,
271 updatedUserGroups))
272 {
273 // Problem in mapping Account Types to User Groups, Error already
274 // logged.
275 return;
276 }
Ginu Georgee93abac2024-06-14 17:35:27 +0530277 setDbusProperty(asyncResp, "AccountTypes",
278 "xyz.openbmc_project.User.Manager", dbusObjectPath,
279 "xyz.openbmc_project.User.Attributes", "UserGroups",
280 updatedUserGroups);
Abhishek Patel58345852022-02-02 08:54:25 -0600281}
282
zhanghch058d1b46d2021-04-01 11:18:24 +0800283inline void userErrorMessageHandler(
284 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
285 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000286{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000287 if (e == nullptr)
288 {
289 messages::internalError(asyncResp->res);
290 return;
291 }
292
Manojkiran Eda055806b2020-11-03 09:36:28 +0530293 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000294 if (strcmp(errorMessage,
295 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
296 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800297 messages::resourceAlreadyExists(asyncResp->res, "ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000298 "UserName", newUser);
299 }
300 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
301 "UserNameDoesNotExist") == 0)
302 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800303 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000304 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700305 else if ((strcmp(errorMessage,
306 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
307 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800308 (strcmp(
309 errorMessage,
310 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
311 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000312 {
313 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
314 }
315 else if (strcmp(errorMessage,
316 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
317 {
318 messages::createLimitReachedForResource(asyncResp->res);
319 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000320 else
321 {
Gunnar Millsb8ad5832023-10-02 16:26:07 -0500322 BMCWEB_LOG_ERROR("DBUS response error {}", errorMessage);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000323 messages::internalError(asyncResp->res);
324 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000325}
326
Ed Tanous81ce6092020-12-17 16:54:55 +0000327inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000328 const LDAPConfigData& confData,
329 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530330{
Ed Tanous49cc2632024-03-20 12:49:15 -0700331 nlohmann::json::object_t ldap;
Ed Tanous14766872022-03-15 10:44:42 -0700332 ldap["ServiceEnabled"] = confData.serviceEnabled;
Ed Tanous49cc2632024-03-20 12:49:15 -0700333 nlohmann::json::array_t serviceAddresses;
Ed Tanous092a33f2025-07-01 11:56:12 -0700334 if (!confData.uri.empty())
335 {
336 serviceAddresses.emplace_back(confData.uri);
337 }
Ed Tanous49cc2632024-03-20 12:49:15 -0700338 ldap["ServiceAddresses"] = std::move(serviceAddresses);
339
340 nlohmann::json::object_t authentication;
341 authentication["AuthenticationType"] =
Ed Tanous0ec8b832022-03-14 14:56:47 -0700342 account_service::AuthenticationTypes::UsernameAndPassword;
Ed Tanous49cc2632024-03-20 12:49:15 -0700343 authentication["Username"] = confData.bindDN;
344 authentication["Password"] = nullptr;
345 ldap["Authentication"] = std::move(authentication);
Ed Tanous14766872022-03-15 10:44:42 -0700346
Ed Tanous49cc2632024-03-20 12:49:15 -0700347 nlohmann::json::object_t ldapService;
348 nlohmann::json::object_t searchSettings;
349 nlohmann::json::array_t baseDistinguishedNames;
Ed Tanous092a33f2025-07-01 11:56:12 -0700350 if (!confData.baseDN.empty())
351 {
352 baseDistinguishedNames.emplace_back(confData.baseDN);
353 }
Ed Tanous14766872022-03-15 10:44:42 -0700354
Ed Tanous49cc2632024-03-20 12:49:15 -0700355 searchSettings["BaseDistinguishedNames"] =
356 std::move(baseDistinguishedNames);
357 searchSettings["UsernameAttribute"] = confData.userNameAttribute;
358 searchSettings["GroupsAttribute"] = confData.groupAttribute;
359 ldapService["SearchSettings"] = std::move(searchSettings);
360 ldap["LDAPService"] = std::move(ldapService);
361
362 nlohmann::json::array_t roleMapArray;
Ed Tanous9eb808c2022-01-25 10:19:23 -0800363 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600364 {
Ed Tanous62598e32023-07-17 17:06:25 -0700365 BMCWEB_LOG_DEBUG("Pushing the data groupName={}", obj.second.groupName);
Ed Tanous613dabe2022-07-09 11:17:36 -0700366
Ed Tanous613dabe2022-07-09 11:17:36 -0700367 nlohmann::json::object_t remoteGroup;
368 remoteGroup["RemoteGroup"] = obj.second.groupName;
Jorge Cisneros329f0342022-11-04 16:26:25 +0000369 remoteGroup["LocalRole"] = getRoleIdFromPrivilege(obj.second.privilege);
370 roleMapArray.emplace_back(std::move(remoteGroup));
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600371 }
Ed Tanous49cc2632024-03-20 12:49:15 -0700372
373 ldap["RemoteRoleMapping"] = std::move(roleMapArray);
374
375 jsonResponse[ldapType].update(ldap);
Ratan Gupta6973a582018-12-13 18:25:44 +0530376}
377
378/**
Ratan Gupta06785242019-07-26 22:30:16 +0530379 * @brief validates given JSON input and then calls appropriate method to
380 * create, to delete or to set Rolemapping object based on the given input.
381 *
382 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000383inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800384 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530385 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousc1019822024-03-06 12:54:38 -0800386 const std::string& serverType,
387 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530388{
389 for (size_t index = 0; index < input.size(); index++)
390 {
Ed Tanousc1019822024-03-06 12:54:38 -0800391 std::variant<nlohmann::json::object_t, std::nullptr_t>& thisJson =
392 input[index];
393 nlohmann::json::object_t* obj =
394 std::get_if<nlohmann::json::object_t>(&thisJson);
395 if (obj == nullptr)
Ratan Gupta06785242019-07-26 22:30:16 +0530396 {
397 // delete the existing object
398 if (index < roleMapObjData.size())
399 {
Ed Tanous177612a2025-02-14 15:16:09 -0800400 dbus::utility::async_method_call(
401 asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530402 [asyncResp, roleMapObjData, serverType,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800403 index](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400404 if (ec)
405 {
406 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
407 messages::internalError(asyncResp->res);
408 return;
409 }
410 asyncResp->res
411 .jsonValue[serverType]["RemoteRoleMapping"][index] =
412 nullptr;
413 },
Ratan Gupta06785242019-07-26 22:30:16 +0530414 ldapDbusService, roleMapObjData[index].first,
415 "xyz.openbmc_project.Object.Delete", "Delete");
416 }
417 else
418 {
Ed Tanous62598e32023-07-17 17:06:25 -0700419 BMCWEB_LOG_ERROR("Can't delete the object");
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400420 messages::propertyValueTypeError(
421 asyncResp->res, "null",
422 "RemoteRoleMapping/" + std::to_string(index));
Ratan Gupta06785242019-07-26 22:30:16 +0530423 return;
424 }
425 }
Ed Tanousc1019822024-03-06 12:54:38 -0800426 else if (obj->empty())
Ratan Gupta06785242019-07-26 22:30:16 +0530427 {
428 // Don't do anything for the empty objects,parse next json
429 // eg {"RemoteRoleMapping",[{}]}
430 }
431 else
432 {
433 // update/create the object
434 std::optional<std::string> remoteGroup;
435 std::optional<std::string> localRole;
436
Patrick Williams504af5a2025-02-03 14:29:03 -0500437 if (!json_util::readJsonObject( //
438 *obj, asyncResp->res, //
439 "LocalRole", localRole, //
Myung Baeafc474a2024-10-09 00:53:29 -0700440 "RemoteGroup", remoteGroup //
441 ))
Ratan Gupta06785242019-07-26 22:30:16 +0530442 {
443 continue;
444 }
445
446 // Update existing RoleMapping Object
447 if (index < roleMapObjData.size())
448 {
Ed Tanous62598e32023-07-17 17:06:25 -0700449 BMCWEB_LOG_DEBUG("Update Role Map Object");
Ratan Gupta06785242019-07-26 22:30:16 +0530450 // If "RemoteGroup" info is provided
451 if (remoteGroup)
452 {
Ed Tanousd02aad32024-02-13 14:43:34 -0800453 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530454 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800455 std::format("RemoteRoleMapping/{}/RemoteGroup", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530456 ldapDbusService, roleMapObjData[index].first,
457 "xyz.openbmc_project.User.PrivilegeMapperEntry",
458 "GroupName", *remoteGroup);
Ratan Gupta06785242019-07-26 22:30:16 +0530459 }
460
461 // If "LocalRole" info is provided
462 if (localRole)
463 {
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500464 std::string priv = getPrivilegeFromRoleId(*localRole);
465 if (priv.empty())
466 {
467 messages::propertyValueNotInList(
468 asyncResp->res, *localRole,
469 std::format("RemoteRoleMapping/{}/LocalRole",
470 index));
471 return;
472 }
Ed Tanousd02aad32024-02-13 14:43:34 -0800473 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530474 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800475 std::format("RemoteRoleMapping/{}/LocalRole", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530476 ldapDbusService, roleMapObjData[index].first,
477 "xyz.openbmc_project.User.PrivilegeMapperEntry",
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500478 "Privilege", priv);
Ratan Gupta06785242019-07-26 22:30:16 +0530479 }
480 }
481 // Create a new RoleMapping Object.
482 else
483 {
Ed Tanous62598e32023-07-17 17:06:25 -0700484 BMCWEB_LOG_DEBUG(
485 "setRoleMappingProperties: Creating new Object");
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400486 std::string pathString =
487 "RemoteRoleMapping/" + std::to_string(index);
Ratan Gupta06785242019-07-26 22:30:16 +0530488
489 if (!localRole)
490 {
491 messages::propertyMissing(asyncResp->res,
492 pathString + "/LocalRole");
493 continue;
494 }
495 if (!remoteGroup)
496 {
497 messages::propertyMissing(asyncResp->res,
498 pathString + "/RemoteGroup");
499 continue;
500 }
501
502 std::string dbusObjectPath;
503 if (serverType == "ActiveDirectory")
504 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700505 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530506 }
507 else if (serverType == "LDAP")
508 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000509 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530510 }
511
Ed Tanous62598e32023-07-17 17:06:25 -0700512 BMCWEB_LOG_DEBUG("Remote Group={},LocalRole={}", *remoteGroup,
513 *localRole);
Ratan Gupta06785242019-07-26 22:30:16 +0530514
Ed Tanous177612a2025-02-14 15:16:09 -0800515 dbus::utility::async_method_call(
516 asyncResp,
Ed Tanous271584a2019-07-09 16:24:22 -0700517 [asyncResp, serverType, localRole,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800518 remoteGroup](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400519 if (ec)
520 {
521 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
522 messages::internalError(asyncResp->res);
523 return;
524 }
525 nlohmann::json& remoteRoleJson =
526 asyncResp->res
527 .jsonValue[serverType]["RemoteRoleMapping"];
528 nlohmann::json::object_t roleMapEntry;
529 roleMapEntry["LocalRole"] = *localRole;
530 roleMapEntry["RemoteGroup"] = *remoteGroup;
531 remoteRoleJson.emplace_back(std::move(roleMapEntry));
532 },
Ratan Gupta06785242019-07-26 22:30:16 +0530533 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700534 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530535 getPrivilegeFromRoleId(std::move(*localRole)));
536 }
537 }
538 }
539}
540
541/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530542 * Function that retrieves all properties for LDAP config object
543 * into JSON
544 */
545template <typename CallbackFunc>
Patrick Williams504af5a2025-02-03 14:29:03 -0500546inline void getLDAPConfigData(const std::string& ldapType,
547 CallbackFunc&& callback)
Ratan Gupta6973a582018-12-13 18:25:44 +0530548{
George Liu2b731192023-01-11 16:27:13 +0800549 constexpr std::array<std::string_view, 2> interfaces = {
550 ldapEnableInterface, ldapConfigInterface};
Ratan Gupta6973a582018-12-13 18:25:44 +0530551
George Liu2b731192023-01-11 16:27:13 +0800552 dbus::utility::getDbusObject(
553 ldapConfigObjectName, interfaces,
Ed Tanous8cb2c022024-03-27 16:31:46 -0700554 [callback = std::forward<CallbackFunc>(callback),
Ed Tanousc1019822024-03-06 12:54:38 -0800555 ldapType](const boost::system::error_code& ec,
556 const dbus::utility::MapperGetObject& resp) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400557 if (ec || resp.empty())
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600558 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400559 BMCWEB_LOG_WARNING(
560 "DBUS response error during getting of service name: {}",
561 ec);
562 LDAPConfigData empty{};
563 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600564 return;
565 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400566 std::string service = resp.begin()->first;
567 sdbusplus::message::object_path path(ldapRootObject);
568 dbus::utility::getManagedObjects(
569 service, path,
570 [callback, ldapType](const boost::system::error_code& ec2,
571 const dbus::utility::ManagedObjectType&
572 ldapObjects) mutable {
573 LDAPConfigData confData{};
574 if (ec2)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600575 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400576 callback(false, confData, ldapType);
577 BMCWEB_LOG_WARNING("D-Bus responses error: {}", ec2);
578 return;
579 }
580
581 std::string ldapDbusType;
582 std::string searchString;
583
584 if (ldapType == "LDAP")
585 {
586 ldapDbusType =
587 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
588 searchString = "openldap";
589 }
590 else if (ldapType == "ActiveDirectory")
591 {
592 ldapDbusType =
593 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
594 searchString = "active_directory";
595 }
596 else
597 {
598 BMCWEB_LOG_ERROR(
599 "Can't get the DbusType for the given type={}",
600 ldapType);
601 callback(false, confData, ldapType);
602 return;
603 }
604
605 std::string ldapEnableInterfaceStr = ldapEnableInterface;
606 std::string ldapConfigInterfaceStr = ldapConfigInterface;
607
608 for (const auto& object : ldapObjects)
609 {
610 // let's find the object whose ldap type is equal to the
611 // given type
612 if (object.first.str.find(searchString) ==
613 std::string::npos)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600614 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400615 continue;
616 }
617
618 for (const auto& interface : object.second)
619 {
620 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600621 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400622 // rest of the properties are string.
623 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600624 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400625 if (property.first == "Enabled")
626 {
627 const bool* value =
628 std::get_if<bool>(&property.second);
629 if (value == nullptr)
630 {
631 continue;
632 }
633 confData.serviceEnabled = *value;
634 break;
635 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600636 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400637 }
638 else if (interface.first == ldapConfigInterfaceStr)
639 {
640 for (const auto& property : interface.second)
641 {
642 const std::string* strValue =
643 std::get_if<std::string>(
644 &property.second);
645 if (strValue == nullptr)
646 {
647 continue;
648 }
649 if (property.first == "LDAPServerURI")
650 {
651 confData.uri = *strValue;
652 }
653 else if (property.first == "LDAPBindDN")
654 {
655 confData.bindDN = *strValue;
656 }
657 else if (property.first == "LDAPBaseDN")
658 {
659 confData.baseDN = *strValue;
660 }
661 else if (property.first ==
662 "LDAPSearchScope")
663 {
664 confData.searchScope = *strValue;
665 }
666 else if (property.first ==
667 "GroupNameAttribute")
668 {
669 confData.groupAttribute = *strValue;
670 }
671 else if (property.first ==
672 "UserNameAttribute")
673 {
674 confData.userNameAttribute = *strValue;
675 }
676 else if (property.first == "LDAPType")
677 {
678 confData.serverType = *strValue;
679 }
680 }
681 }
682 else if (
683 interface.first ==
684 "xyz.openbmc_project.User.PrivilegeMapperEntry")
685 {
686 LDAPRoleMapData roleMapData{};
687 for (const auto& property : interface.second)
688 {
689 const std::string* strValue =
690 std::get_if<std::string>(
691 &property.second);
692
693 if (strValue == nullptr)
694 {
695 continue;
696 }
697
698 if (property.first == "GroupName")
699 {
700 roleMapData.groupName = *strValue;
701 }
702 else if (property.first == "Privilege")
703 {
704 roleMapData.privilege = *strValue;
705 }
706 }
707
708 confData.groupRoleList.emplace_back(
709 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600710 }
711 }
712 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400713 callback(true, confData, ldapType);
714 });
George Liu2b731192023-01-11 16:27:13 +0800715 });
Ratan Gupta6973a582018-12-13 18:25:44 +0530716}
717
Ed Tanous6c51eab2021-06-03 12:30:29 -0700718/**
Ed Tanous6c51eab2021-06-03 12:30:29 -0700719 * @brief updates the LDAP server address and updates the
720 json response with the new value.
721 * @param serviceAddressList address to be updated.
722 * @param asyncResp pointer to the JSON response
723 * @param ldapServerElementName Type of LDAP
724 server(openLDAP/ActiveDirectory)
725 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530726
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700727inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700728 const std::vector<std::string>& serviceAddressList,
729 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
730 const std::string& ldapServerElementName,
731 const std::string& ldapConfigObject)
732{
Ginu Georgee93abac2024-06-14 17:35:27 +0530733 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceAddress",
734 ldapDbusService, ldapConfigObject, ldapConfigInterface,
735 "LDAPServerURI", serviceAddressList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700736}
737/**
738 * @brief updates the LDAP Bind DN and updates the
739 json response with the new value.
740 * @param username name of the user which needs to be updated.
741 * @param asyncResp pointer to the JSON response
742 * @param ldapServerElementName Type of LDAP
743 server(openLDAP/ActiveDirectory)
744 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530745
Patrick Williams504af5a2025-02-03 14:29:03 -0500746inline void handleUserNamePatch(
747 const std::string& username,
748 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
749 const std::string& ldapServerElementName,
750 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700751{
Ginu Georgee93abac2024-06-14 17:35:27 +0530752 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800753 ldapServerElementName + "/Authentication/Username",
Ginu Georgee93abac2024-06-14 17:35:27 +0530754 ldapDbusService, ldapConfigObject, ldapConfigInterface,
755 "LDAPBindDN", username);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700756}
757
758/**
759 * @brief updates the LDAP password
760 * @param password : ldap password which needs to be updated.
761 * @param asyncResp pointer to the JSON response
762 * @param ldapServerElementName Type of LDAP
763 * server(openLDAP/ActiveDirectory)
764 */
765
Patrick Williams504af5a2025-02-03 14:29:03 -0500766inline void handlePasswordPatch(
767 const std::string& password,
768 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
769 const std::string& ldapServerElementName,
770 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700771{
Ginu Georgee93abac2024-06-14 17:35:27 +0530772 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800773 ldapServerElementName + "/Authentication/Password",
Ginu Georgee93abac2024-06-14 17:35:27 +0530774 ldapDbusService, ldapConfigObject, ldapConfigInterface,
775 "LDAPBindDNPassword", password);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700776}
777
778/**
779 * @brief updates the LDAP BaseDN and updates the
780 json response with the new value.
781 * @param baseDNList baseDN list which needs to be updated.
782 * @param asyncResp pointer to the JSON response
783 * @param ldapServerElementName Type of LDAP
784 server(openLDAP/ActiveDirectory)
785 */
786
Patrick Williams504af5a2025-02-03 14:29:03 -0500787inline void handleBaseDNPatch(
788 const std::vector<std::string>& baseDNList,
789 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
790 const std::string& ldapServerElementName,
791 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700792{
Ginu Georgee93abac2024-06-14 17:35:27 +0530793 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800794 ldapServerElementName +
795 "/LDAPService/SearchSettings/BaseDistinguishedNames",
Ginu Georgee93abac2024-06-14 17:35:27 +0530796 ldapDbusService, ldapConfigObject, ldapConfigInterface,
797 "LDAPBaseDN", baseDNList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700798}
799/**
800 * @brief updates the LDAP user name attribute and updates the
801 json response with the new value.
802 * @param userNameAttribute attribute to be updated.
803 * @param asyncResp pointer to the JSON response
804 * @param ldapServerElementName Type of LDAP
805 server(openLDAP/ActiveDirectory)
806 */
807
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400808inline void handleUserNameAttrPatch(
809 const std::string& userNameAttribute,
810 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
811 const std::string& ldapServerElementName,
812 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700813{
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400814 setDbusProperty(
815 asyncResp,
816 ldapServerElementName + "LDAPService/SearchSettings/UsernameAttribute",
817 ldapDbusService, ldapConfigObject, ldapConfigInterface,
818 "UserNameAttribute", userNameAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700819}
820/**
821 * @brief updates the LDAP group attribute and updates the
822 json response with the new value.
823 * @param groupsAttribute attribute to be updated.
824 * @param asyncResp pointer to the JSON response
825 * @param ldapServerElementName Type of LDAP
826 server(openLDAP/ActiveDirectory)
827 */
828
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700829inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700830 const std::string& groupsAttribute,
831 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
832 const std::string& ldapServerElementName,
833 const std::string& ldapConfigObject)
834{
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400835 setDbusProperty(
836 asyncResp,
837 ldapServerElementName + "/LDAPService/SearchSettings/GroupsAttribute",
838 ldapDbusService, ldapConfigObject, ldapConfigInterface,
839 "GroupNameAttribute", groupsAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700840}
841/**
842 * @brief updates the LDAP service enable and updates the
843 json response with the new value.
844 * @param input JSON data.
845 * @param asyncResp pointer to the JSON response
846 * @param ldapServerElementName Type of LDAP
847 server(openLDAP/ActiveDirectory)
848 */
849
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700850inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700851 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
852 const std::string& ldapServerElementName,
853 const std::string& ldapConfigObject)
854{
Ginu Georgee93abac2024-06-14 17:35:27 +0530855 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceEnabled",
856 ldapDbusService, ldapConfigObject, ldapEnableInterface,
857 "Enabled", serviceEnabled);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700858}
859
Ed Tanousc1019822024-03-06 12:54:38 -0800860struct AuthMethods
Ed Tanous6c51eab2021-06-03 12:30:29 -0700861{
862 std::optional<bool> basicAuth;
863 std::optional<bool> cookie;
864 std::optional<bool> sessionToken;
865 std::optional<bool> xToken;
866 std::optional<bool> tls;
Ed Tanousc1019822024-03-06 12:54:38 -0800867};
Ed Tanous6c51eab2021-06-03 12:30:29 -0700868
Patrick Williams504af5a2025-02-03 14:29:03 -0500869inline void handleAuthMethodsPatch(
870 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
871 const AuthMethods& auth)
Ed Tanousc1019822024-03-06 12:54:38 -0800872{
873 persistent_data::AuthConfigMethods& authMethodsConfig =
Ed Tanous6c51eab2021-06-03 12:30:29 -0700874 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
875
Ed Tanousc1019822024-03-06 12:54:38 -0800876 if (auth.basicAuth)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700877 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700878 if constexpr (!BMCWEB_BASIC_AUTH)
879 {
880 messages::actionNotSupported(
881 asyncResp->res,
882 "Setting BasicAuth when basic-auth feature is disabled");
883 return;
884 }
885
Ed Tanousc1019822024-03-06 12:54:38 -0800886 authMethodsConfig.basic = *auth.basicAuth;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700887 }
888
Ed Tanousc1019822024-03-06 12:54:38 -0800889 if (auth.cookie)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700890 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700891 if constexpr (!BMCWEB_COOKIE_AUTH)
892 {
893 messages::actionNotSupported(
894 asyncResp->res,
895 "Setting Cookie when cookie-auth feature is disabled");
896 return;
897 }
Ed Tanousc1019822024-03-06 12:54:38 -0800898 authMethodsConfig.cookie = *auth.cookie;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700899 }
900
Ed Tanousc1019822024-03-06 12:54:38 -0800901 if (auth.sessionToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700902 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700903 if constexpr (!BMCWEB_SESSION_AUTH)
904 {
905 messages::actionNotSupported(
906 asyncResp->res,
907 "Setting SessionToken when session-auth feature is disabled");
908 return;
909 }
Ed Tanousc1019822024-03-06 12:54:38 -0800910 authMethodsConfig.sessionToken = *auth.sessionToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700911 }
912
Ed Tanousc1019822024-03-06 12:54:38 -0800913 if (auth.xToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700914 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700915 if constexpr (!BMCWEB_XTOKEN_AUTH)
916 {
917 messages::actionNotSupported(
918 asyncResp->res,
919 "Setting XToken when xtoken-auth feature is disabled");
920 return;
921 }
Ed Tanousc1019822024-03-06 12:54:38 -0800922 authMethodsConfig.xtoken = *auth.xToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700923 }
924
Ed Tanousc1019822024-03-06 12:54:38 -0800925 if (auth.tls)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700926 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700927 if constexpr (!BMCWEB_MUTUAL_TLS_AUTH)
928 {
929 messages::actionNotSupported(
930 asyncResp->res,
931 "Setting TLS when mutual-tls-auth feature is disabled");
932 return;
933 }
Ed Tanousc1019822024-03-06 12:54:38 -0800934 authMethodsConfig.tls = *auth.tls;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700935 }
936
937 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
938 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
939 !authMethodsConfig.tls)
940 {
941 // Do not allow user to disable everything
942 messages::actionNotSupported(asyncResp->res,
943 "of disabling all available methods");
944 return;
945 }
946
947 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
948 authMethodsConfig);
949 // Save configuration immediately
950 persistent_data::getConfig().writeData();
951
952 messages::success(asyncResp->res);
953}
954
955/**
956 * @brief Get the required values from the given JSON, validates the
957 * value and create the LDAP config object.
958 * @param input JSON data
959 * @param asyncResp pointer to the JSON response
960 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
961 */
962
Ed Tanous10cb44f2024-04-11 13:05:20 -0700963struct LdapPatchParams
964{
965 std::optional<std::string> authType;
966 std::optional<std::vector<std::string>> serviceAddressList;
967 std::optional<bool> serviceEnabled;
968 std::optional<std::vector<std::string>> baseDNList;
969 std::optional<std::string> userNameAttribute;
970 std::optional<std::string> groupsAttribute;
971 std::optional<std::string> userName;
972 std::optional<std::string> password;
973 std::optional<
974 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>
975 remoteRoleMapData;
976};
977
978inline void handleLDAPPatch(LdapPatchParams&& input,
Ed Tanous6c51eab2021-06-03 12:30:29 -0700979 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
980 const std::string& serverType)
981{
982 std::string dbusObjectPath;
983 if (serverType == "ActiveDirectory")
984 {
985 dbusObjectPath = adConfigObject;
986 }
987 else if (serverType == "LDAP")
988 {
989 dbusObjectPath = ldapConfigObjectName;
990 }
991 else
992 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700993 BMCWEB_LOG_ERROR("serverType wasn't AD or LDAP but was {}????",
994 serverType);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700995 return;
996 }
997
Ed Tanous10cb44f2024-04-11 13:05:20 -0700998 if (input.authType && *input.authType != "UsernameAndPassword")
Ed Tanous6c51eab2021-06-03 12:30:29 -0700999 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001000 messages::propertyValueNotInList(asyncResp->res, *input.authType,
Ed Tanousc1019822024-03-06 12:54:38 -08001001 "AuthenticationType");
1002 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001003 }
Ed Tanousc1019822024-03-06 12:54:38 -08001004
Ed Tanous10cb44f2024-04-11 13:05:20 -07001005 if (input.serviceAddressList)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001006 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001007 if (input.serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301008 {
Ed Tanouse2616cc2022-06-27 12:45:55 -07001009 messages::propertyValueNotInList(
Ed Tanous10cb44f2024-04-11 13:05:20 -07001010 asyncResp->res, *input.serviceAddressList, "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001011 return;
1012 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001013 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001014 if (input.baseDNList)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001015 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001016 if (input.baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +05301017 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001018 messages::propertyValueNotInList(asyncResp->res, *input.baseDNList,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001019 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301020 return;
1021 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001022 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301023
Ed Tanous6c51eab2021-06-03 12:30:29 -07001024 // nothing to update, then return
Ed Tanous10cb44f2024-04-11 13:05:20 -07001025 if (!input.userName && !input.password && !input.serviceAddressList &&
1026 !input.baseDNList && !input.userNameAttribute &&
1027 !input.groupsAttribute && !input.serviceEnabled &&
1028 !input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001029 {
1030 return;
1031 }
1032
1033 // Get the existing resource first then keep modifying
1034 // whenever any property gets updated.
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001035 getLDAPConfigData(serverType, [asyncResp, input = std::move(input),
1036 dbusObjectPath = std::move(dbusObjectPath)](
1037 bool success,
1038 const LDAPConfigData& confData,
1039 const std::string& serverT) mutable {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001040 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301041 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001042 messages::internalError(asyncResp->res);
1043 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301044 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001045 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1046 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301047 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001048 // Disable the service first and update the rest of
1049 // the properties.
1050 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301051 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001052
Ed Tanous10cb44f2024-04-11 13:05:20 -07001053 if (input.serviceAddressList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301054 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001055 handleServiceAddressPatch(*input.serviceAddressList, asyncResp,
1056 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301057 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001058 if (input.userName)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001059 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001060 handleUserNamePatch(*input.userName, asyncResp, serverT,
1061 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001062 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001063 if (input.password)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001064 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001065 handlePasswordPatch(*input.password, asyncResp, serverT,
1066 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001067 }
1068
Ed Tanous10cb44f2024-04-11 13:05:20 -07001069 if (input.baseDNList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301070 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001071 handleBaseDNPatch(*input.baseDNList, asyncResp, serverT,
1072 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001073 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001074 if (input.userNameAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001075 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001076 handleUserNameAttrPatch(*input.userNameAttribute, asyncResp,
1077 serverT, dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001078 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001079 if (input.groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001080 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001081 handleGroupNameAttrPatch(*input.groupsAttribute, asyncResp, serverT,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001082 dbusObjectPath);
1083 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001084 if (input.serviceEnabled)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001085 {
1086 // if user has given the value as true then enable
1087 // the service. if user has given false then no-op
1088 // as service is already stopped.
Ed Tanous10cb44f2024-04-11 13:05:20 -07001089 if (*input.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301090 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001091 handleServiceEnablePatch(*input.serviceEnabled, asyncResp,
1092 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301093 }
1094 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001095 else
1096 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001097 // if user has not given the service enabled value
1098 // then revert it to the same state as it was
1099 // before.
1100 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1101 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001102 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001103
Ed Tanous10cb44f2024-04-11 13:05:20 -07001104 if (input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001105 {
1106 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001107 *input.remoteRoleMapData);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001108 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001109 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001110}
1111
Ed Tanous492ec932024-12-09 15:42:19 -08001112struct UserUpdateParams
1113{
1114 std::string username;
1115 std::optional<std::string> password;
1116 std::optional<bool> enabled;
1117 std::optional<std::string> roleId;
1118 std::optional<bool> locked;
1119 std::optional<std::vector<std::string>> accountTypes;
1120 bool userSelf;
1121 std::shared_ptr<persistent_data::UserSession> session;
1122 std::string dbusObjectPath;
1123};
1124
Patrick Williams504af5a2025-02-03 14:29:03 -05001125inline void afterVerifyUserExists(
1126 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1127 const UserUpdateParams& params, int rc)
Ed Tanous492ec932024-12-09 15:42:19 -08001128{
1129 if (rc <= 0)
1130 {
1131 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1132 params.username);
1133 return;
1134 }
1135
1136 if (params.password)
1137 {
1138 int retval = pamUpdatePassword(params.username, *params.password);
1139
1140 if (retval == PAM_USER_UNKNOWN)
1141 {
1142 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1143 params.username);
1144 }
1145 else if (retval == PAM_AUTHTOK_ERR)
1146 {
1147 // If password is invalid
1148 messages::propertyValueFormatError(asyncResp->res, nullptr,
1149 "Password");
1150 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
1151 }
1152 else if (retval != PAM_SUCCESS)
1153 {
1154 messages::internalError(asyncResp->res);
1155 return;
1156 }
1157 else
1158 {
1159 // Remove existing sessions of the user when password
1160 // changed
1161 persistent_data::SessionStore::getInstance()
1162 .removeSessionsByUsernameExceptSession(params.username,
1163 params.session);
1164 messages::success(asyncResp->res);
1165 }
1166 }
1167
1168 if (params.enabled)
1169 {
1170 setDbusProperty(
1171 asyncResp, "Enabled", "xyz.openbmc_project.User.Manager",
1172 params.dbusObjectPath, "xyz.openbmc_project.User.Attributes",
1173 "UserEnabled", *params.enabled);
1174 }
1175
1176 if (params.roleId)
1177 {
1178 std::string priv = getPrivilegeFromRoleId(*params.roleId);
1179 if (priv.empty())
1180 {
1181 messages::propertyValueNotInList(asyncResp->res, true, "Locked");
1182 return;
1183 }
1184 setDbusProperty(asyncResp, "RoleId", "xyz.openbmc_project.User.Manager",
1185 params.dbusObjectPath,
1186 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1187 priv);
1188 }
1189
1190 if (params.locked)
1191 {
1192 // admin can unlock the account which is locked by
1193 // successive authentication failures but admin should
1194 // not be allowed to lock an account.
1195 if (*params.locked)
1196 {
1197 messages::propertyValueNotInList(asyncResp->res, "true", "Locked");
1198 return;
1199 }
1200 setDbusProperty(asyncResp, "Locked", "xyz.openbmc_project.User.Manager",
1201 params.dbusObjectPath,
1202 "xyz.openbmc_project.User.Attributes",
1203 "UserLockedForFailedAttempt", *params.locked);
1204 }
1205
1206 if (params.accountTypes)
1207 {
1208 patchAccountTypes(*params.accountTypes, asyncResp,
1209 params.dbusObjectPath, params.userSelf);
1210 }
1211}
1212
Abhishek Patel58345852022-02-02 08:54:25 -06001213inline void updateUserProperties(
Ed Tanous492ec932024-12-09 15:42:19 -08001214 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1215 const std::string& username, const std::optional<std::string>& password,
Abhishek Patel58345852022-02-02 08:54:25 -06001216 const std::optional<bool>& enabled,
1217 const std::optional<std::string>& roleId, const std::optional<bool>& locked,
Ed Tanous492ec932024-12-09 15:42:19 -08001218 const std::optional<std::vector<std::string>>& accountTypes, bool userSelf,
Ravi Tejae518ef32024-05-16 10:33:08 -05001219 const std::shared_ptr<persistent_data::UserSession>& session)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001220{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301221 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1222 tempObjPath /= username;
1223 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001224
Ed Tanous492ec932024-12-09 15:42:19 -08001225 UserUpdateParams params{username, password, enabled,
1226 roleId, locked, accountTypes,
1227 userSelf, session, dbusObjectPath};
1228
Ed Tanous6c51eab2021-06-03 12:30:29 -07001229 dbus::utility::checkDbusPathExists(
Ravi Tejae518ef32024-05-16 10:33:08 -05001230 dbusObjectPath,
Ed Tanous492ec932024-12-09 15:42:19 -08001231 std::bind_front(afterVerifyUserExists, asyncResp, std::move(params)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001232}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001233
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001234inline void handleAccountServiceHead(
1235 App& app, const crow::Request& req,
1236 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001237{
1238 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1239 {
1240 return;
1241 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001242 asyncResp->res.addHeader(
1243 boost::beast::http::field::link,
1244 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1245}
1246
Patrick Williams504af5a2025-02-03 14:29:03 -05001247inline void getClientCertificates(
1248 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1249 const nlohmann::json::json_pointer& keyLocation)
Ed Tanous1aa375b2024-04-13 11:51:10 -07001250{
1251 boost::urls::url url(
1252 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates");
1253 std::array<std::string_view, 1> interfaces = {
1254 "xyz.openbmc_project.Certs.Certificate"};
1255 std::string path = "/xyz/openbmc_project/certs/authority/truststore";
1256
1257 collection_util::getCollectionToKey(asyncResp, url, interfaces, path,
1258 keyLocation);
1259}
1260
1261inline void handleAccountServiceClientCertificatesInstanceHead(
1262 App& app, const crow::Request& req,
1263 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1264 const std::string& /*id*/)
1265{
1266 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1267 {
1268 return;
1269 }
1270
1271 asyncResp->res.addHeader(
1272 boost::beast::http::field::link,
1273 "</redfish/v1/JsonSchemas/Certificate/Certificate.json>; rel=describedby");
1274}
1275
1276inline void handleAccountServiceClientCertificatesInstanceGet(
1277 App& app, const crow::Request& req,
1278 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1279{
1280 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1281 {
1282 return;
1283 }
1284 BMCWEB_LOG_DEBUG("ClientCertificate Certificate ID={}", id);
1285 const boost::urls::url certURL = boost::urls::format(
1286 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/{}",
1287 id);
1288 std::string objPath =
1289 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1290 getCertificateProperties(
1291 asyncResp, objPath,
1292 "xyz.openbmc_project.Certs.Manager.Authority.Truststore", id, certURL,
1293 "Client Certificate");
1294}
1295
1296inline void handleAccountServiceClientCertificatesHead(
1297 App& app, const crow::Request& req,
1298 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1299{
1300 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1301 {
1302 return;
1303 }
1304
1305 asyncResp->res.addHeader(
1306 boost::beast::http::field::link,
1307 "</redfish/v1/JsonSchemas/CertificateCollection/CertificateCollection.json>; rel=describedby");
1308}
1309
1310inline void handleAccountServiceClientCertificatesGet(
1311 App& app, const crow::Request& req,
1312 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1313{
1314 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1315 {
1316 return;
1317 }
Myung Bae0f09ed32025-03-31 15:32:24 -05001318
1319 nlohmann::json& json = asyncResp->res.jsonValue;
1320 json["@odata.id"] =
1321 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates";
1322 json["@odata.type"] = "#CertificateCollection.CertificateCollection";
1323 json["Name"] = "Certificates Collection";
1324 json["Description"] = "Multi-factor Authentication Client Certificates";
Ed Tanous1aa375b2024-04-13 11:51:10 -07001325 getClientCertificates(asyncResp, "/Members"_json_pointer);
1326}
1327
Ed Tanous3ce36882024-06-09 10:58:16 -07001328using account_service::CertificateMappingAttribute;
1329using persistent_data::MTLSCommonNameParseMode;
Patrick Williams504af5a2025-02-03 14:29:03 -05001330inline CertificateMappingAttribute getCertificateMapping(
1331 MTLSCommonNameParseMode parse)
Ed Tanous3ce36882024-06-09 10:58:16 -07001332{
1333 switch (parse)
1334 {
1335 case MTLSCommonNameParseMode::CommonName:
1336 {
1337 return CertificateMappingAttribute::CommonName;
1338 }
1339 break;
1340 case MTLSCommonNameParseMode::Whole:
1341 {
1342 return CertificateMappingAttribute::Whole;
1343 }
1344 break;
1345 case MTLSCommonNameParseMode::UserPrincipalName:
1346 {
1347 return CertificateMappingAttribute::UserPrincipalName;
1348 }
1349 break;
Ed Tanous3ce36882024-06-09 10:58:16 -07001350 default:
1351 {
1352 return CertificateMappingAttribute::Invalid;
1353 }
1354 break;
1355 }
1356}
1357
Patrick Williams504af5a2025-02-03 14:29:03 -05001358inline void handleAccountServiceGet(
1359 App& app, const crow::Request& req,
1360 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001361{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001362 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1363 {
1364 return;
1365 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001366
1367 if (req.session == nullptr)
1368 {
1369 messages::internalError(asyncResp->res);
1370 return;
1371 }
1372
Ed Tanousc1019822024-03-06 12:54:38 -08001373 const persistent_data::AuthConfigMethods& authMethodsConfig =
1374 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1375
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001376 asyncResp->res.addHeader(
1377 boost::beast::http::field::link,
1378 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1379
Ed Tanous1ef4c342022-05-12 16:12:36 -07001380 nlohmann::json& json = asyncResp->res.jsonValue;
1381 json["@odata.id"] = "/redfish/v1/AccountService";
Ravi Teja482a69e2024-04-22 06:56:13 -05001382 json["@odata.type"] = "#AccountService.v1_15_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001383 json["Id"] = "AccountService";
1384 json["Name"] = "Account Service";
1385 json["Description"] = "Account Service";
1386 json["ServiceEnabled"] = true;
1387 json["MaxPasswordLength"] = 20;
1388 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1389 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
Ravi Teja482a69e2024-04-22 06:56:13 -05001390 json["HTTPBasicAuth"] = authMethodsConfig.basic
1391 ? account_service::BasicAuthState::Enabled
1392 : account_service::BasicAuthState::Disabled;
1393
1394 nlohmann::json::array_t allowed;
1395 allowed.emplace_back(account_service::BasicAuthState::Enabled);
1396 allowed.emplace_back(account_service::BasicAuthState::Disabled);
1397 json["HTTPBasicAuth@AllowableValues"] = std::move(allowed);
1398
Ed Tanous1aa375b2024-04-13 11:51:10 -07001399 nlohmann::json::object_t clientCertificate;
1400 clientCertificate["Enabled"] = authMethodsConfig.tls;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001401 clientCertificate["RespondToUnauthenticatedClients"] =
1402 !authMethodsConfig.tlsStrict;
Ed Tanous3ce36882024-06-09 10:58:16 -07001403
1404 using account_service::CertificateMappingAttribute;
1405
1406 CertificateMappingAttribute mapping =
1407 getCertificateMapping(authMethodsConfig.mTLSCommonNameParsingMode);
1408 if (mapping == CertificateMappingAttribute::Invalid)
1409 {
1410 messages::internalError(asyncResp->res);
1411 }
1412 else
1413 {
1414 clientCertificate["CertificateMappingAttribute"] = mapping;
1415 }
Ed Tanous1aa375b2024-04-13 11:51:10 -07001416 nlohmann::json::object_t certificates;
1417 certificates["@odata.id"] =
1418 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates";
1419 certificates["@odata.type"] =
1420 "#CertificateCollection.CertificateCollection";
1421 clientCertificate["Certificates"] = std::move(certificates);
1422 json["MultiFactorAuth"]["ClientCertificate"] = std::move(clientCertificate);
1423
1424 getClientCertificates(
1425 asyncResp,
1426 "/MultiFactorAuth/ClientCertificate/Certificates/Members"_json_pointer);
1427
Ed Tanous1ef4c342022-05-12 16:12:36 -07001428 json["Oem"]["OpenBMC"]["@odata.type"] =
Ed Tanous5b5574a2022-09-26 19:53:36 -07001429 "#OpenBMCAccountService.v1_0_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001430 json["Oem"]["OpenBMC"]["@odata.id"] =
1431 "/redfish/v1/AccountService#/Oem/OpenBMC";
1432 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1433 authMethodsConfig.basic;
1434 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1435 authMethodsConfig.sessionToken;
1436 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1437 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1438 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1439
1440 // /redfish/v1/AccountService/LDAP/Certificates is something only
1441 // ConfigureManager can access then only display when the user has
1442 // permissions ConfigureManager
1443 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001444 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001445
1446 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1447 effectiveUserPrivileges))
1448 {
1449 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1450 "/redfish/v1/AccountService/LDAP/Certificates";
1451 }
Ed Tanousdeae6a72024-11-11 21:58:57 -08001452 dbus::utility::getAllProperties(
1453 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1454 "xyz.openbmc_project.User.AccountPolicy",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001455 [asyncResp](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001456 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001457 if (ec)
1458 {
1459 messages::internalError(asyncResp->res);
1460 return;
1461 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001462
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001463 BMCWEB_LOG_DEBUG("Got {} properties for AccountService",
1464 propertiesList.size());
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001465
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001466 const uint8_t* minPasswordLength = nullptr;
1467 const uint32_t* accountUnlockTimeout = nullptr;
1468 const uint16_t* maxLoginAttemptBeforeLockout = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001469
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001470 const bool success = sdbusplus::unpackPropertiesNoThrow(
1471 dbus_utils::UnpackErrorPrinter(), propertiesList,
1472 "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout",
1473 accountUnlockTimeout, "MaxLoginAttemptBeforeLockout",
1474 maxLoginAttemptBeforeLockout);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001475
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001476 if (!success)
1477 {
1478 messages::internalError(asyncResp->res);
1479 return;
1480 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001481
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001482 if (minPasswordLength != nullptr)
1483 {
1484 asyncResp->res.jsonValue["MinPasswordLength"] =
1485 *minPasswordLength;
1486 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001487
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001488 if (accountUnlockTimeout != nullptr)
1489 {
1490 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1491 *accountUnlockTimeout;
1492 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001493
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001494 if (maxLoginAttemptBeforeLockout != nullptr)
1495 {
1496 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1497 *maxLoginAttemptBeforeLockout;
1498 }
1499 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001500
Ed Tanous02cad962022-06-30 16:50:15 -07001501 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001502 const std::string& ldapType) {
1503 if (!success)
1504 {
1505 return;
1506 }
1507 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1508 };
1509
1510 getLDAPConfigData("LDAP", callback);
1511 getLDAPConfigData("ActiveDirectory", callback);
1512}
1513
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001514inline void handleCertificateMappingAttributePatch(
1515 crow::Response& res, const std::string& certMapAttribute)
Ed Tanous3ce36882024-06-09 10:58:16 -07001516{
1517 MTLSCommonNameParseMode parseMode =
1518 persistent_data::getMTLSCommonNameParseMode(certMapAttribute);
1519 if (parseMode == MTLSCommonNameParseMode::Invalid)
1520 {
1521 messages::propertyValueNotInList(res, "CertificateMappingAttribute",
1522 certMapAttribute);
1523 return;
1524 }
1525
1526 persistent_data::AuthConfigMethods& authMethodsConfig =
1527 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1528 authMethodsConfig.mTLSCommonNameParsingMode = parseMode;
1529}
1530
Ed Tanous3281bcf2024-06-25 16:02:05 -07001531inline void handleRespondToUnauthenticatedClientsPatch(
1532 App& app, const crow::Request& req, crow::Response& res,
1533 bool respondToUnauthenticatedClients)
1534{
1535 if (req.session != nullptr)
1536 {
1537 // Sanity check. If the user isn't currently authenticated with mutual
1538 // TLS, they very likely are about to permanently lock themselves out.
1539 // Make sure they're using mutual TLS before allowing locking.
1540 if (req.session->sessionType != persistent_data::SessionType::MutualTLS)
1541 {
1542 messages::propertyValueExternalConflict(
1543 res,
1544 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
1545 respondToUnauthenticatedClients);
1546 return;
1547 }
1548 }
1549
1550 persistent_data::AuthConfigMethods& authMethodsConfig =
1551 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1552
1553 // Change the settings
1554 authMethodsConfig.tlsStrict = !respondToUnauthenticatedClients;
1555
1556 // Write settings to disk
1557 persistent_data::getConfig().writeData();
1558
1559 // Trigger a reload, to apply the new settings to new connections
1560 app.loadCertificate();
1561}
1562
Ed Tanous1ef4c342022-05-12 16:12:36 -07001563inline void handleAccountServicePatch(
1564 App& app, const crow::Request& req,
1565 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1566{
1567 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1568 {
1569 return;
1570 }
1571 std::optional<uint32_t> unlockTimeout;
1572 std::optional<uint16_t> lockoutThreshold;
1573 std::optional<uint8_t> minPasswordLength;
1574 std::optional<uint16_t> maxPasswordLength;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001575 LdapPatchParams ldapObject;
Ed Tanous3ce36882024-06-09 10:58:16 -07001576 std::optional<std::string> certificateMappingAttribute;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001577 std::optional<bool> respondToUnauthenticatedClients;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001578 LdapPatchParams activeDirectoryObject;
Ed Tanousc1019822024-03-06 12:54:38 -08001579 AuthMethods auth;
Ravi Teja482a69e2024-04-22 06:56:13 -05001580 std::optional<std::string> httpBasicAuth;
Ed Tanous3ce36882024-06-09 10:58:16 -07001581
Patrick Williams504af5a2025-02-03 14:29:03 -05001582 if (!json_util::readJsonPatch( //
1583 req, asyncResp->res, //
1584 "AccountLockoutDuration", unlockTimeout, //
1585 "AccountLockoutThreshold", lockoutThreshold, //
Myung Baeafc474a2024-10-09 00:53:29 -07001586 "ActiveDirectory/Authentication/AuthenticationType",
Patrick Williams504af5a2025-02-03 14:29:03 -05001587 activeDirectoryObject.authType, //
Myung Baeafc474a2024-10-09 00:53:29 -07001588 "ActiveDirectory/Authentication/Password",
Patrick Williams504af5a2025-02-03 14:29:03 -05001589 activeDirectoryObject.password, //
Myung Baeafc474a2024-10-09 00:53:29 -07001590 "ActiveDirectory/Authentication/Username",
Patrick Williams504af5a2025-02-03 14:29:03 -05001591 activeDirectoryObject.userName, //
Myung Baeafc474a2024-10-09 00:53:29 -07001592 "ActiveDirectory/LDAPService/SearchSettings/BaseDistinguishedNames",
Patrick Williams504af5a2025-02-03 14:29:03 -05001593 activeDirectoryObject.baseDNList, //
Myung Baeafc474a2024-10-09 00:53:29 -07001594 "ActiveDirectory/LDAPService/SearchSettings/GroupsAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001595 activeDirectoryObject.groupsAttribute, //
Myung Baeafc474a2024-10-09 00:53:29 -07001596 "ActiveDirectory/LDAPService/SearchSettings/UsernameAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001597 activeDirectoryObject.userNameAttribute, //
Myung Baeafc474a2024-10-09 00:53:29 -07001598 "ActiveDirectory/RemoteRoleMapping",
Patrick Williams504af5a2025-02-03 14:29:03 -05001599 activeDirectoryObject.remoteRoleMapData, //
Myung Baeafc474a2024-10-09 00:53:29 -07001600 "ActiveDirectory/ServiceAddresses",
Patrick Williams504af5a2025-02-03 14:29:03 -05001601 activeDirectoryObject.serviceAddressList, //
Myung Baeafc474a2024-10-09 00:53:29 -07001602 "ActiveDirectory/ServiceEnabled",
Patrick Williams504af5a2025-02-03 14:29:03 -05001603 activeDirectoryObject.serviceEnabled, //
1604 "HTTPBasicAuth", httpBasicAuth, //
Myung Baeafc474a2024-10-09 00:53:29 -07001605 "LDAP/Authentication/AuthenticationType", ldapObject.authType, //
Patrick Williams504af5a2025-02-03 14:29:03 -05001606 "LDAP/Authentication/Password", ldapObject.password, //
1607 "LDAP/Authentication/Username", ldapObject.userName, //
Myung Baeafc474a2024-10-09 00:53:29 -07001608 "LDAP/LDAPService/SearchSettings/BaseDistinguishedNames",
Patrick Williams504af5a2025-02-03 14:29:03 -05001609 ldapObject.baseDNList, //
Myung Baeafc474a2024-10-09 00:53:29 -07001610 "LDAP/LDAPService/SearchSettings/GroupsAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001611 ldapObject.groupsAttribute, //
Myung Baeafc474a2024-10-09 00:53:29 -07001612 "LDAP/LDAPService/SearchSettings/UsernameAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001613 ldapObject.userNameAttribute, //
1614 "LDAP/RemoteRoleMapping", ldapObject.remoteRoleMapData, //
1615 "LDAP/ServiceAddresses", ldapObject.serviceAddressList, //
1616 "LDAP/ServiceEnabled", ldapObject.serviceEnabled, //
1617 "MaxPasswordLength", maxPasswordLength, //
1618 "MinPasswordLength", minPasswordLength, //
Myung Baeafc474a2024-10-09 00:53:29 -07001619 "MultiFactorAuth/ClientCertificate/CertificateMappingAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001620 certificateMappingAttribute, //
Myung Baeafc474a2024-10-09 00:53:29 -07001621 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
Patrick Williams504af5a2025-02-03 14:29:03 -05001622 respondToUnauthenticatedClients, //
1623 "Oem/OpenBMC/AuthMethods/BasicAuth", auth.basicAuth, //
1624 "Oem/OpenBMC/AuthMethods/Cookie", auth.cookie, //
1625 "Oem/OpenBMC/AuthMethods/SessionToken", auth.sessionToken, //
1626 "Oem/OpenBMC/AuthMethods/TLS", auth.tls, //
1627 "Oem/OpenBMC/AuthMethods/XToken", auth.xToken //
Myung Baeafc474a2024-10-09 00:53:29 -07001628 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001629 {
1630 return;
1631 }
1632
Ravi Teja482a69e2024-04-22 06:56:13 -05001633 if (httpBasicAuth)
1634 {
1635 if (*httpBasicAuth == "Enabled")
1636 {
1637 auth.basicAuth = true;
1638 }
1639 else if (*httpBasicAuth == "Disabled")
1640 {
1641 auth.basicAuth = false;
1642 }
1643 else
1644 {
1645 messages::propertyValueNotInList(asyncResp->res, "HttpBasicAuth",
1646 *httpBasicAuth);
1647 }
1648 }
1649
Ed Tanous3281bcf2024-06-25 16:02:05 -07001650 if (respondToUnauthenticatedClients)
1651 {
1652 handleRespondToUnauthenticatedClientsPatch(
1653 app, req, asyncResp->res, *respondToUnauthenticatedClients);
1654 }
1655
Ed Tanous3ce36882024-06-09 10:58:16 -07001656 if (certificateMappingAttribute)
1657 {
1658 handleCertificateMappingAttributePatch(asyncResp->res,
1659 *certificateMappingAttribute);
1660 }
1661
Ed Tanous1ef4c342022-05-12 16:12:36 -07001662 if (minPasswordLength)
1663 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001664 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301665 asyncResp, "MinPasswordLength", "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001666 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001667 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
Ginu Georgee93abac2024-06-14 17:35:27 +05301668 *minPasswordLength);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001669 }
1670
1671 if (maxPasswordLength)
1672 {
1673 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1674 }
1675
Ed Tanous10cb44f2024-04-11 13:05:20 -07001676 handleLDAPPatch(std::move(activeDirectoryObject), asyncResp,
1677 "ActiveDirectory");
1678 handleLDAPPatch(std::move(ldapObject), asyncResp, "LDAP");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001679
Ed Tanousc1019822024-03-06 12:54:38 -08001680 handleAuthMethodsPatch(asyncResp, auth);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001681
Ed Tanous1ef4c342022-05-12 16:12:36 -07001682 if (unlockTimeout)
1683 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001684 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301685 asyncResp, "AccountLockoutDuration",
1686 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001687 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
Ed Tanous1ef4c342022-05-12 16:12:36 -07001688 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
Ginu Georgee93abac2024-06-14 17:35:27 +05301689 *unlockTimeout);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001690 }
1691 if (lockoutThreshold)
1692 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001693 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301694 asyncResp, "AccountLockoutThreshold",
1695 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001696 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001697 "xyz.openbmc_project.User.AccountPolicy",
Ginu Georgee93abac2024-06-14 17:35:27 +05301698 "MaxLoginAttemptBeforeLockout", *lockoutThreshold);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001699 }
1700}
1701
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001702inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001703 App& app, const crow::Request& req,
1704 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1705{
1706 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1707 {
1708 return;
1709 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001710 asyncResp->res.addHeader(
1711 boost::beast::http::field::link,
1712 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1713}
1714
1715inline void handleAccountCollectionGet(
1716 App& app, const crow::Request& req,
1717 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1718{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001719 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1720 {
1721 return;
1722 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001723
1724 if (req.session == nullptr)
1725 {
1726 messages::internalError(asyncResp->res);
1727 return;
1728 }
1729
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001730 asyncResp->res.addHeader(
1731 boost::beast::http::field::link,
1732 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001733
1734 asyncResp->res.jsonValue["@odata.id"] =
1735 "/redfish/v1/AccountService/Accounts";
1736 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1737 "ManagerAccountCollection";
1738 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1739 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1740
1741 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001742 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001743
1744 std::string thisUser;
1745 if (req.session)
1746 {
1747 thisUser = req.session->username;
1748 }
George Liu5eb468d2023-06-20 17:03:24 +08001749 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
1750 dbus::utility::getManagedObjects(
1751 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001752 [asyncResp, thisUser, effectiveUserPrivileges](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001753 const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001754 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001755 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001756 {
1757 messages::internalError(asyncResp->res);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001758 return;
1759 }
1760
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001761 bool userCanSeeAllAccounts =
1762 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1763
1764 bool userCanSeeSelf =
1765 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1766
1767 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1768 memberArray = nlohmann::json::array();
1769
1770 for (const auto& userpath : users)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001771 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001772 std::string user = userpath.first.filename();
1773 if (user.empty())
1774 {
1775 messages::internalError(asyncResp->res);
1776 BMCWEB_LOG_ERROR("Invalid firmware ID");
1777
1778 return;
1779 }
1780
1781 // As clarified by Redfish here:
1782 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1783 // Users without ConfigureUsers, only see their own
1784 // account. Users with ConfigureUsers, see all
1785 // accounts.
1786 if (userCanSeeAllAccounts ||
1787 (thisUser == user && userCanSeeSelf))
1788 {
1789 nlohmann::json::object_t member;
1790 member["@odata.id"] = boost::urls::format(
1791 "/redfish/v1/AccountService/Accounts/{}", user);
1792 memberArray.emplace_back(std::move(member));
1793 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001794 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001795 asyncResp->res.jsonValue["Members@odata.count"] =
1796 memberArray.size();
1797 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001798}
1799
Ninad Palsule97e90da2023-05-17 14:04:52 -05001800inline void processAfterCreateUser(
1801 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1802 const std::string& username, const std::string& password,
1803 const boost::system::error_code& ec, sdbusplus::message_t& m)
1804{
1805 if (ec)
1806 {
1807 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1808 return;
1809 }
1810
1811 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1812 {
1813 // At this point we have a user that's been
1814 // created, but the password set
1815 // failed.Something is wrong, so delete the user
1816 // that we've already created
1817 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1818 tempObjPath /= username;
1819 const std::string userPath(tempObjPath);
1820
Ed Tanous177612a2025-02-14 15:16:09 -08001821 dbus::utility::async_method_call(
1822 asyncResp,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001823 [asyncResp, password](const boost::system::error_code& ec3) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001824 if (ec3)
1825 {
1826 messages::internalError(asyncResp->res);
1827 return;
1828 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001829
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001830 // If password is invalid
1831 messages::propertyValueFormatError(asyncResp->res, nullptr,
1832 "Password");
1833 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001834 "xyz.openbmc_project.User.Manager", userPath,
1835 "xyz.openbmc_project.Object.Delete", "Delete");
1836
Ed Tanous62598e32023-07-17 17:06:25 -07001837 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
Ninad Palsule97e90da2023-05-17 14:04:52 -05001838 return;
1839 }
1840
1841 messages::created(asyncResp->res);
1842 asyncResp->res.addHeader("Location",
1843 "/redfish/v1/AccountService/Accounts/" + username);
1844}
1845
1846inline void processAfterGetAllGroups(
1847 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1848 const std::string& username, const std::string& password,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001849 const std::string& roleId, bool enabled,
Ninad Palsule9ba73932023-06-01 16:38:57 -05001850 std::optional<std::vector<std::string>> accountTypes,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001851 const std::vector<std::string>& allGroupsList)
Ninad Palsule97e90da2023-05-17 14:04:52 -05001852{
Ninad Palsule3e72c202023-03-27 17:19:55 -05001853 std::vector<std::string> userGroups;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001854 std::vector<std::string> accountTypeUserGroups;
1855
1856 // If user specified account types then convert them to unix user groups
1857 if (accountTypes)
1858 {
1859 if (!getUserGroupFromAccountType(asyncResp->res, *accountTypes,
1860 accountTypeUserGroups))
1861 {
1862 // Problem in mapping Account Types to User Groups, Error already
1863 // logged.
1864 return;
1865 }
1866 }
1867
Ninad Palsule3e72c202023-03-27 17:19:55 -05001868 for (const auto& grp : allGroupsList)
1869 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001870 // If user specified the account type then only accept groups which are
1871 // in the account types group list.
1872 if (!accountTypeUserGroups.empty())
1873 {
1874 bool found = false;
1875 for (const auto& grp1 : accountTypeUserGroups)
1876 {
1877 if (grp == grp1)
1878 {
1879 found = true;
1880 break;
1881 }
1882 }
1883 if (!found)
1884 {
1885 continue;
1886 }
1887 }
1888
Ninad Palsule3e72c202023-03-27 17:19:55 -05001889 // Console access is provided to the user who is a member of
1890 // hostconsole group and has a administrator role. So, set
1891 // hostconsole group only for the administrator.
Ninad Palsule9ba73932023-06-01 16:38:57 -05001892 if ((grp == "hostconsole") && (roleId != "priv-admin"))
Ninad Palsule3e72c202023-03-27 17:19:55 -05001893 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001894 if (!accountTypeUserGroups.empty())
1895 {
Ed Tanous62598e32023-07-17 17:06:25 -07001896 BMCWEB_LOG_ERROR(
1897 "Only administrator can get HostConsole access");
Ninad Palsule9ba73932023-06-01 16:38:57 -05001898 asyncResp->res.result(boost::beast::http::status::bad_request);
1899 return;
1900 }
1901 continue;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001902 }
Ninad Palsule9ba73932023-06-01 16:38:57 -05001903 userGroups.emplace_back(grp);
1904 }
1905
1906 // Make sure user specified groups are valid. This is internal error because
1907 // it some inconsistencies between user manager and bmcweb.
1908 if (!accountTypeUserGroups.empty() &&
1909 accountTypeUserGroups.size() != userGroups.size())
1910 {
1911 messages::internalError(asyncResp->res);
1912 return;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001913 }
Ed Tanous177612a2025-02-14 15:16:09 -08001914 dbus::utility::async_method_call(
1915 asyncResp,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001916 [asyncResp, username, password](const boost::system::error_code& ec2,
1917 sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001918 processAfterCreateUser(asyncResp, username, password, ec2, m);
1919 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001920 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ninad Palsule3e72c202023-03-27 17:19:55 -05001921 "xyz.openbmc_project.User.Manager", "CreateUser", username, userGroups,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001922 roleId, enabled);
Ninad Palsule97e90da2023-05-17 14:04:52 -05001923}
1924
Ed Tanous1ef4c342022-05-12 16:12:36 -07001925inline void handleAccountCollectionPost(
1926 App& app, const crow::Request& req,
1927 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1928{
1929 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1930 {
1931 return;
1932 }
1933 std::string username;
1934 std::string password;
Ed Tanouse01d0c32023-06-30 13:21:32 -07001935 std::optional<std::string> roleIdJson;
1936 std::optional<bool> enabledJson;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001937 std::optional<std::vector<std::string>> accountTypes;
Patrick Williams504af5a2025-02-03 14:29:03 -05001938 if (!json_util::readJsonPatch( //
1939 req, asyncResp->res, //
Myung Baeafc474a2024-10-09 00:53:29 -07001940 "AccountTypes", accountTypes, //
Patrick Williams504af5a2025-02-03 14:29:03 -05001941 "Enabled", enabledJson, //
1942 "Password", password, //
1943 "RoleId", roleIdJson, //
1944 "UserName", username //
Myung Baeafc474a2024-10-09 00:53:29 -07001945 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001946 {
1947 return;
1948 }
1949
Ed Tanouse01d0c32023-06-30 13:21:32 -07001950 std::string roleId = roleIdJson.value_or("User");
1951 std::string priv = getPrivilegeFromRoleId(roleId);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001952 if (priv.empty())
1953 {
Ed Tanouse01d0c32023-06-30 13:21:32 -07001954 messages::propertyValueNotInList(asyncResp->res, roleId, "RoleId");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001955 return;
1956 }
Asmitha Karunanithi239adf82022-03-25 02:59:03 -05001957 roleId = priv;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001958
Ed Tanouse01d0c32023-06-30 13:21:32 -07001959 bool enabled = enabledJson.value_or(true);
1960
Ed Tanous1ef4c342022-05-12 16:12:36 -07001961 // Reading AllGroups property
Ed Tanousdeae6a72024-11-11 21:58:57 -08001962 dbus::utility::getProperty<std::vector<std::string>>(
1963 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1964 "xyz.openbmc_project.User.Manager", "AllGroups",
Ninad Palsule9ba73932023-06-01 16:38:57 -05001965 [asyncResp, username, password{std::move(password)}, roleId, enabled,
1966 accountTypes](const boost::system::error_code& ec,
1967 const std::vector<std::string>& allGroupsList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001968 if (ec)
1969 {
Gunnar Millsa0735a42024-09-06 12:51:11 -05001970 BMCWEB_LOG_ERROR("D-Bus response error {}", ec);
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001971 messages::internalError(asyncResp->res);
1972 return;
1973 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001974
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001975 if (allGroupsList.empty())
1976 {
1977 messages::internalError(asyncResp->res);
1978 return;
1979 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001980
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001981 processAfterGetAllGroups(asyncResp, username, password, roleId,
1982 enabled, accountTypes, allGroupsList);
1983 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001984}
1985
Patrick Williams504af5a2025-02-03 14:29:03 -05001986inline void handleAccountHead(
1987 App& app, const crow::Request& req,
1988 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1989 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001990{
1991 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1992 {
1993 return;
1994 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001995 asyncResp->res.addHeader(
1996 boost::beast::http::field::link,
1997 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1998}
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001999
Patrick Williams504af5a2025-02-03 14:29:03 -05002000inline void handleAccountGet(
2001 App& app, const crow::Request& req,
2002 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2003 const std::string& accountName)
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002004{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08002005 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2006 {
2007 return;
2008 }
2009 asyncResp->res.addHeader(
2010 boost::beast::http::field::link,
2011 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
2012
Ed Tanous25b54db2024-04-17 15:40:31 -07002013 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2014 {
2015 // If authentication is disabled, there are no user accounts
2016 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2017 accountName);
2018 return;
2019 }
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08002020
Ed Tanous1ef4c342022-05-12 16:12:36 -07002021 if (req.session == nullptr)
2022 {
2023 messages::internalError(asyncResp->res);
2024 return;
2025 }
2026 if (req.session->username != accountName)
2027 {
2028 // At this point we've determined that the user is trying to
2029 // modify a user that isn't them. We need to verify that they
2030 // have permissions to modify other users, so re-run the auth
2031 // check with the same permissions, minus ConfigureSelf.
2032 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002033 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002034 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
2035 "ConfigureManager"};
2036 if (!effectiveUserPrivileges.isSupersetOf(
2037 requiredPermissionsToChangeNonSelf))
2038 {
Ed Tanous62598e32023-07-17 17:06:25 -07002039 BMCWEB_LOG_DEBUG("GET Account denied access");
Ed Tanous1ef4c342022-05-12 16:12:36 -07002040 messages::insufficientPrivilege(asyncResp->res);
2041 return;
2042 }
2043 }
2044
George Liu5eb468d2023-06-20 17:03:24 +08002045 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
2046 dbus::utility::getManagedObjects(
2047 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07002048 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002049 accountName](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07002050 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002051 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002052 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002053 messages::internalError(asyncResp->res);
2054 return;
2055 }
2056 const auto userIt = std::ranges::find_if(
2057 users,
2058 [accountName](
2059 const std::pair<sdbusplus::message::object_path,
2060 dbus::utility::DBusInterfacesMap>& user) {
2061 return accountName == user.first.filename();
2062 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002063
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002064 if (userIt == users.end())
2065 {
2066 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2067 accountName);
2068 return;
2069 }
2070
2071 asyncResp->res.jsonValue["@odata.type"] =
2072 "#ManagerAccount.v1_7_0.ManagerAccount";
2073 asyncResp->res.jsonValue["Name"] = "User Account";
2074 asyncResp->res.jsonValue["Description"] = "User Account";
2075 asyncResp->res.jsonValue["Password"] = nullptr;
2076 asyncResp->res.jsonValue["StrictAccountTypes"] = true;
2077
2078 for (const auto& interface : userIt->second)
2079 {
2080 if (interface.first == "xyz.openbmc_project.User.Attributes")
2081 {
Asmitha Karunanithib437a532024-12-20 04:54:25 -06002082 const bool* userEnabled = nullptr;
2083 const bool* userLocked = nullptr;
2084 const std::string* userPrivPtr = nullptr;
2085 const bool* userPasswordExpired = nullptr;
2086 const std::vector<std::string>* userGroups = nullptr;
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002087
Asmitha Karunanithib437a532024-12-20 04:54:25 -06002088 const bool success = sdbusplus::unpackPropertiesNoThrow(
2089 dbus_utils::UnpackErrorPrinter(), interface.second,
2090 "UserEnabled", userEnabled,
2091 "UserLockedForFailedAttempt", userLocked,
2092 "UserPrivilege", userPrivPtr, "UserPasswordExpired",
2093 userPasswordExpired, "UserGroups", userGroups);
2094 if (!success)
2095 {
2096 messages::internalError(asyncResp->res);
2097 return;
2098 }
2099 if (userEnabled == nullptr)
2100 {
2101 BMCWEB_LOG_ERROR("UserEnabled wasn't a bool");
2102 messages::internalError(asyncResp->res);
2103 return;
2104 }
2105 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
2106
2107 if (userLocked == nullptr)
2108 {
2109 BMCWEB_LOG_ERROR("UserLockedForF"
2110 "ailedAttempt "
2111 "wasn't a bool");
2112 messages::internalError(asyncResp->res);
2113 return;
2114 }
2115 asyncResp->res.jsonValue["Locked"] = *userLocked;
2116 nlohmann::json::array_t allowed;
2117 // can only unlock accounts
2118 allowed.emplace_back("false");
2119 asyncResp->res.jsonValue["Locked@Redfish.AllowableValues"] =
2120 std::move(allowed);
2121
2122 if (userPrivPtr == nullptr)
2123 {
2124 BMCWEB_LOG_ERROR("UserPrivilege wasn't a "
2125 "string");
2126 messages::internalError(asyncResp->res);
2127 return;
2128 }
2129 std::string role = getRoleIdFromPrivilege(*userPrivPtr);
2130 if (role.empty())
2131 {
2132 BMCWEB_LOG_ERROR("Invalid user role");
2133 messages::internalError(asyncResp->res);
2134 return;
2135 }
2136 asyncResp->res.jsonValue["RoleId"] = role;
2137
2138 nlohmann::json& roleEntry =
2139 asyncResp->res.jsonValue["Links"]["Role"];
2140 roleEntry["@odata.id"] = boost::urls::format(
2141 "/redfish/v1/AccountService/Roles/{}", role);
2142
2143 if (userPasswordExpired == nullptr)
2144 {
2145 BMCWEB_LOG_ERROR("UserPasswordExpired wasn't a bool");
2146 messages::internalError(asyncResp->res);
2147 return;
2148 }
2149 asyncResp->res.jsonValue["PasswordChangeRequired"] =
2150 *userPasswordExpired;
2151
2152 if (userGroups == nullptr)
2153 {
2154 BMCWEB_LOG_ERROR("userGroups wasn't a string vector");
2155 messages::internalError(asyncResp->res);
2156 return;
2157 }
2158 if (!translateUserGroup(*userGroups, asyncResp->res))
2159 {
2160 BMCWEB_LOG_ERROR("userGroups mapping failed");
2161 messages::internalError(asyncResp->res);
2162 return;
Abhishek Patelc7229812022-02-01 10:07:15 -06002163 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002164 }
2165 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002166
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002167 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
2168 "/redfish/v1/AccountService/Accounts/{}", accountName);
2169 asyncResp->res.jsonValue["Id"] = accountName;
2170 asyncResp->res.jsonValue["UserName"] = accountName;
2171 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002172}
2173
Patrick Williams504af5a2025-02-03 14:29:03 -05002174inline void handleAccountDelete(
2175 App& app, const crow::Request& req,
2176 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2177 const std::string& username)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002178{
2179 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2180 {
2181 return;
2182 }
2183
Ed Tanous25b54db2024-04-17 15:40:31 -07002184 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2185 {
2186 // If authentication is disabled, there are no user accounts
2187 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2188 return;
2189 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002190 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
2191 tempObjPath /= username;
2192 const std::string userPath(tempObjPath);
2193
Ed Tanous177612a2025-02-14 15:16:09 -08002194 dbus::utility::async_method_call(
2195 asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002196 [asyncResp, username](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002197 if (ec)
2198 {
2199 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2200 username);
2201 return;
2202 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002203
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002204 messages::accountRemoved(asyncResp->res);
2205 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002206 "xyz.openbmc_project.User.Manager", userPath,
2207 "xyz.openbmc_project.Object.Delete", "Delete");
2208}
2209
Patrick Williams504af5a2025-02-03 14:29:03 -05002210inline void handleAccountPatch(
2211 App& app, const crow::Request& req,
2212 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2213 const std::string& username)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002214{
2215 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2216 {
2217 return;
2218 }
Ed Tanous25b54db2024-04-17 15:40:31 -07002219 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2220 {
2221 // If authentication is disabled, there are no user accounts
2222 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2223 return;
2224 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002225 std::optional<std::string> newUserName;
2226 std::optional<std::string> password;
2227 std::optional<bool> enabled;
2228 std::optional<std::string> roleId;
2229 std::optional<bool> locked;
Abhishek Patel58345852022-02-02 08:54:25 -06002230 std::optional<std::vector<std::string>> accountTypes;
2231
Ed Tanous1ef4c342022-05-12 16:12:36 -07002232 if (req.session == nullptr)
2233 {
2234 messages::internalError(asyncResp->res);
2235 return;
2236 }
2237
Ed Tanous2b9c1df2024-04-06 13:52:01 -07002238 bool userSelf = (username == req.session->username);
2239
Ed Tanous1ef4c342022-05-12 16:12:36 -07002240 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002241 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002242 Privileges configureUsers = {"ConfigureUsers"};
2243 bool userHasConfigureUsers =
2244 effectiveUserPrivileges.isSupersetOf(configureUsers);
2245 if (userHasConfigureUsers)
2246 {
2247 // Users with ConfigureUsers can modify for all users
Patrick Williams504af5a2025-02-03 14:29:03 -05002248 if (!json_util::readJsonPatch( //
2249 req, asyncResp->res, //
Myung Baeafc474a2024-10-09 00:53:29 -07002250 "AccountTypes", accountTypes, //
Patrick Williams504af5a2025-02-03 14:29:03 -05002251 "Enabled", enabled, //
2252 "Locked", locked, //
2253 "Password", password, //
2254 "RoleId", roleId, //
2255 "UserName", newUserName //
Myung Baeafc474a2024-10-09 00:53:29 -07002256 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07002257 {
2258 return;
2259 }
2260 }
2261 else
2262 {
2263 // ConfigureSelf accounts can only modify their own account
Abhishek Patel58345852022-02-02 08:54:25 -06002264 if (!userSelf)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002265 {
2266 messages::insufficientPrivilege(asyncResp->res);
2267 return;
2268 }
2269
2270 // ConfigureSelf accounts can only modify their password
2271 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
2272 password))
2273 {
2274 return;
2275 }
2276 }
2277
2278 // if user name is not provided in the patch method or if it
2279 // matches the user name in the URI, then we are treating it as
2280 // updating user properties other then username. If username
2281 // provided doesn't match the URI, then we are treating this as
2282 // user rename request.
2283 if (!newUserName || (newUserName.value() == username))
2284 {
2285 updateUserProperties(asyncResp, username, password, enabled, roleId,
Ravi Tejae518ef32024-05-16 10:33:08 -05002286 locked, accountTypes, userSelf, req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002287 return;
2288 }
Ed Tanous177612a2025-02-14 15:16:09 -08002289 dbus::utility::async_method_call(
2290 asyncResp,
Ed Tanous1ef4c342022-05-12 16:12:36 -07002291 [asyncResp, username, password(std::move(password)),
2292 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Ed Tanous608ad2c2024-05-20 19:14:50 -07002293 locked, userSelf, session = req.session,
2294 accountTypes(std::move(accountTypes))](
Ed Tanouse81de512023-06-27 17:07:00 -07002295 const boost::system::error_code& ec, sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002296 if (ec)
2297 {
2298 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
2299 username);
2300 return;
2301 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002302
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002303 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
Ed Tanous608ad2c2024-05-20 19:14:50 -07002304 locked, accountTypes, userSelf, session);
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002305 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002306 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
2307 "xyz.openbmc_project.User.Manager", "RenameUser", username,
2308 *newUserName);
2309}
2310
Ed Tanous6c51eab2021-06-03 12:30:29 -07002311inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07002312{
Ed Tanous6c51eab2021-06-03 12:30:29 -07002313 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002314 .privileges(redfish::privileges::headAccountService)
2315 .methods(boost::beast::http::verb::head)(
2316 std::bind_front(handleAccountServiceHead, std::ref(app)));
2317
2318 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07002319 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002320 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002321 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002322
Ed Tanousf5ffd802021-07-19 10:55:33 -07002323 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06002324 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07002325 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002326 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07002327
Ed Tanous1aa375b2024-04-13 11:51:10 -07002328 BMCWEB_ROUTE(
2329 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002330 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002331 .privileges(redfish::privileges::headCertificateCollection)
2332 .methods(boost::beast::http::verb::head)(std::bind_front(
2333 handleAccountServiceClientCertificatesHead, std::ref(app)));
2334
2335 BMCWEB_ROUTE(
2336 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002337 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002338 .privileges(redfish::privileges::getCertificateCollection)
2339 .methods(boost::beast::http::verb::get)(std::bind_front(
2340 handleAccountServiceClientCertificatesGet, std::ref(app)));
2341
2342 BMCWEB_ROUTE(
2343 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002344 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002345 .privileges(redfish::privileges::headCertificate)
2346 .methods(boost::beast::http::verb::head)(std::bind_front(
2347 handleAccountServiceClientCertificatesInstanceHead, std::ref(app)));
2348
2349 BMCWEB_ROUTE(
2350 app,
2351 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
2352 .privileges(redfish::privileges::getCertificate)
2353 .methods(boost::beast::http::verb::get)(std::bind_front(
2354 handleAccountServiceClientCertificatesInstanceGet, std::ref(app)));
2355
Ed Tanous6c51eab2021-06-03 12:30:29 -07002356 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002357 .privileges(redfish::privileges::headManagerAccountCollection)
2358 .methods(boost::beast::http::verb::head)(
2359 std::bind_front(handleAccountCollectionHead, std::ref(app)));
2360
2361 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002362 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002363 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002364 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07002365
Ed Tanous6c51eab2021-06-03 12:30:29 -07002366 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002367 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002368 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002369 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07002370
2371 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002372 .privileges(redfish::privileges::headManagerAccount)
2373 .methods(boost::beast::http::verb::head)(
2374 std::bind_front(handleAccountHead, std::ref(app)));
2375
2376 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07002377 .privileges(redfish::privileges::getManagerAccount)
2378 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002379 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002380
2381 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002382 // TODO this privilege should be using the generated endpoints, but
2383 // because of the special handling of ConfigureSelf, it's not able to
2384 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07002385 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
2386 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002387 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002388
2389 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002390 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002391 .methods(boost::beast::http::verb::delete_)(
Gunnar Mills20fc3072023-01-27 15:13:36 -06002392 std::bind_front(handleAccountDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002393}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002394
Ed Tanous1abe55e2018-09-05 08:30:59 -07002395} // namespace redfish