blob: 8b312b185a17eb37f173550b2d8142ab154ef784 [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 Tanousd7857202025-01-28 15:32:26 -080012#include "dbus_singleton.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080013#include "dbus_utility.hpp"
14#include "error_messages.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070015#include "generated/enums/account_service.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080016#include "http_request.hpp"
17#include "http_response.hpp"
18#include "logging.hpp"
19#include "pam_authenticate.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080020#include "persistent_data.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080021#include "privileges.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080022#include "query.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070023#include "registries/privilege_registry.hpp"
Ed Tanous3281bcf2024-06-25 16:02:05 -070024#include "sessions.hpp"
Ed Tanous1aa375b2024-04-13 11:51:10 -070025#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080026#include "utils/dbus_utils.hpp"
27#include "utils/json_utils.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070028
Ed Tanousd7857202025-01-28 15:32:26 -080029#include <security/_pam_types.h>
30#include <systemd/sd-bus.h>
31
32#include <boost/beast/http/field.hpp>
33#include <boost/beast/http/status.hpp>
34#include <boost/beast/http/verb.hpp>
Ed Tanous1aa375b2024-04-13 11:51:10 -070035#include <boost/url/format.hpp>
36#include <boost/url/url.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080037#include <nlohmann/json.hpp>
38#include <sdbusplus/message.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020039#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050040
Ed Tanousd7857202025-01-28 15:32:26 -080041#include <algorithm>
George Liu2b731192023-01-11 16:27:13 +080042#include <array>
Ed Tanousd7857202025-01-28 15:32:26 -080043#include <cstddef>
44#include <cstdint>
45#include <cstring>
46#include <format>
47#include <functional>
Ed Tanous1aa375b2024-04-13 11:51:10 -070048#include <memory>
Abhishek Patelc7229812022-02-01 10:07:15 -060049#include <optional>
Ed Tanous3544d2a2023-08-06 18:12:20 -070050#include <ranges>
Abhishek Patelc7229812022-02-01 10:07:15 -060051#include <string>
George Liu2b731192023-01-11 16:27:13 +080052#include <string_view>
Ed Tanous20fa6a22024-05-20 18:02:58 -070053#include <utility>
Ed Tanousd7857202025-01-28 15:32:26 -080054#include <variant>
Abhishek Patelc7229812022-02-01 10:07:15 -060055#include <vector>
56
Ed Tanous1abe55e2018-09-05 08:30:59 -070057namespace redfish
58{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010059
Ed Tanous23a21a12020-07-25 04:45:05 +000060constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053061 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070062constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053063 "/xyz/openbmc_project/user/ldap/active_directory";
64
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053065constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053066constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
67constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
68constexpr const char* ldapConfigInterface =
69 "xyz.openbmc_project.User.Ldap.Config";
70constexpr const char* ldapCreateInterface =
71 "xyz.openbmc_project.User.Ldap.Create";
72constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053073constexpr const char* ldapPrivMapperInterface =
74 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053075
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060076struct LDAPRoleMapData
77{
78 std::string groupName;
79 std::string privilege;
80};
81
Ratan Gupta6973a582018-12-13 18:25:44 +053082struct LDAPConfigData
83{
Ed Tanous47f29342024-03-19 12:18:06 -070084 std::string uri;
85 std::string bindDN;
86 std::string baseDN;
87 std::string searchScope;
88 std::string serverType;
Ratan Gupta6973a582018-12-13 18:25:44 +053089 bool serviceEnabled = false;
Ed Tanous47f29342024-03-19 12:18:06 -070090 std::string userNameAttribute;
91 std::string groupAttribute;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060092 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053093};
94
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060095inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053096{
97 if (role == "priv-admin")
98 {
99 return "Administrator";
100 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700101 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530102 {
AppaRao Pulic80fee52019-10-16 14:49:36 +0530103 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530104 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700105 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530106 {
107 return "Operator";
108 }
109 return "";
110}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600111inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530112{
113 if (role == "Administrator")
114 {
115 return "priv-admin";
116 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700117 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530118 {
119 return "priv-user";
120 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700121 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530122 {
123 return "priv-operator";
124 }
125 return "";
126}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700127
Abhishek Patelc7229812022-02-01 10:07:15 -0600128/**
129 * @brief Maps user group names retrieved from D-Bus object to
130 * Account Types.
131 *
132 * @param[in] userGroups List of User groups
133 * @param[out] res AccountTypes populated
134 *
135 * @return true in case of success, false if UserGroups contains
136 * invalid group name(s).
137 */
138inline bool translateUserGroup(const std::vector<std::string>& userGroups,
139 crow::Response& res)
140{
141 std::vector<std::string> accountTypes;
142 for (const auto& userGroup : userGroups)
143 {
144 if (userGroup == "redfish")
145 {
146 accountTypes.emplace_back("Redfish");
147 accountTypes.emplace_back("WebUI");
148 }
149 else if (userGroup == "ipmi")
150 {
151 accountTypes.emplace_back("IPMI");
152 }
153 else if (userGroup == "ssh")
154 {
Abhishek Patelc7229812022-02-01 10:07:15 -0600155 accountTypes.emplace_back("ManagerConsole");
156 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500157 else if (userGroup == "hostconsole")
158 {
159 // The hostconsole group controls who can access the host console
160 // port via ssh and websocket.
161 accountTypes.emplace_back("HostConsole");
162 }
Abhishek Patelc7229812022-02-01 10:07:15 -0600163 else if (userGroup == "web")
164 {
165 // 'web' is one of the valid groups in the UserGroups property of
166 // the user account in the D-Bus object. This group is currently not
167 // doing anything, and is considered to be equivalent to 'redfish'.
168 // 'redfish' user group is mapped to 'Redfish'and 'WebUI'
169 // AccountTypes, so do nothing here...
170 }
171 else
172 {
Ed Tanous8ece0e42024-01-02 13:16:50 -0800173 // Invalid user group name. Caller throws an exception.
Abhishek Patelc7229812022-02-01 10:07:15 -0600174 return false;
175 }
176 }
177
178 res.jsonValue["AccountTypes"] = std::move(accountTypes);
179 return true;
180}
181
Abhishek Patel58345852022-02-02 08:54:25 -0600182/**
183 * @brief Builds User Groups from the Account Types
184 *
185 * @param[in] asyncResp Async Response
186 * @param[in] accountTypes List of Account Types
187 * @param[out] userGroups List of User Groups mapped from Account Types
188 *
189 * @return true if Account Types mapped to User Groups, false otherwise.
190 */
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400191inline bool getUserGroupFromAccountType(
192 crow::Response& res, const std::vector<std::string>& accountTypes,
193 std::vector<std::string>& userGroups)
Abhishek Patel58345852022-02-02 08:54:25 -0600194{
195 // Need both Redfish and WebUI Account Types to map to 'redfish' User Group
196 bool redfishType = false;
197 bool webUIType = false;
198
199 for (const auto& accountType : accountTypes)
200 {
201 if (accountType == "Redfish")
202 {
203 redfishType = true;
204 }
205 else if (accountType == "WebUI")
206 {
207 webUIType = true;
208 }
209 else if (accountType == "IPMI")
210 {
211 userGroups.emplace_back("ipmi");
212 }
213 else if (accountType == "HostConsole")
214 {
215 userGroups.emplace_back("hostconsole");
216 }
217 else if (accountType == "ManagerConsole")
218 {
219 userGroups.emplace_back("ssh");
220 }
221 else
222 {
223 // Invalid Account Type
224 messages::propertyValueNotInList(res, "AccountTypes", accountType);
225 return false;
226 }
227 }
228
229 // Both Redfish and WebUI Account Types are needed to PATCH
230 if (redfishType ^ webUIType)
231 {
Ed Tanous62598e32023-07-17 17:06:25 -0700232 BMCWEB_LOG_ERROR(
233 "Missing Redfish or WebUI Account Type to set redfish User Group");
Abhishek Patel58345852022-02-02 08:54:25 -0600234 messages::strictAccountTypes(res, "AccountTypes");
235 return false;
236 }
237
238 if (redfishType && webUIType)
239 {
240 userGroups.emplace_back("redfish");
241 }
242
243 return true;
244}
245
246/**
247 * @brief Sets UserGroups property of the user based on the Account Types
248 *
249 * @param[in] accountTypes List of User Account Types
250 * @param[in] asyncResp Async Response
251 * @param[in] dbusObjectPath D-Bus Object Path
252 * @param[in] userSelf true if User is updating OWN Account Types
253 */
Patrick Williams504af5a2025-02-03 14:29:03 -0500254inline void patchAccountTypes(
255 const std::vector<std::string>& accountTypes,
256 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
257 const std::string& dbusObjectPath, bool userSelf)
Abhishek Patel58345852022-02-02 08:54:25 -0600258{
259 // Check if User is disabling own Redfish Account Type
260 if (userSelf &&
261 (accountTypes.cend() ==
262 std::find(accountTypes.cbegin(), accountTypes.cend(), "Redfish")))
263 {
Ed Tanous62598e32023-07-17 17:06:25 -0700264 BMCWEB_LOG_ERROR(
265 "User disabling OWN Redfish Account Type is not allowed");
Abhishek Patel58345852022-02-02 08:54:25 -0600266 messages::strictAccountTypes(asyncResp->res, "AccountTypes");
267 return;
268 }
269
270 std::vector<std::string> updatedUserGroups;
271 if (!getUserGroupFromAccountType(asyncResp->res, accountTypes,
272 updatedUserGroups))
273 {
274 // Problem in mapping Account Types to User Groups, Error already
275 // logged.
276 return;
277 }
Ginu Georgee93abac2024-06-14 17:35:27 +0530278 setDbusProperty(asyncResp, "AccountTypes",
279 "xyz.openbmc_project.User.Manager", dbusObjectPath,
280 "xyz.openbmc_project.User.Attributes", "UserGroups",
281 updatedUserGroups);
Abhishek Patel58345852022-02-02 08:54:25 -0600282}
283
zhanghch058d1b46d2021-04-01 11:18:24 +0800284inline void userErrorMessageHandler(
285 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
286 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000287{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000288 if (e == nullptr)
289 {
290 messages::internalError(asyncResp->res);
291 return;
292 }
293
Manojkiran Eda055806b2020-11-03 09:36:28 +0530294 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000295 if (strcmp(errorMessage,
296 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
297 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800298 messages::resourceAlreadyExists(asyncResp->res, "ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000299 "UserName", newUser);
300 }
301 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
302 "UserNameDoesNotExist") == 0)
303 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800304 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000305 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700306 else if ((strcmp(errorMessage,
307 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
308 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800309 (strcmp(
310 errorMessage,
311 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
312 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000313 {
314 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
315 }
316 else if (strcmp(errorMessage,
317 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
318 {
319 messages::createLimitReachedForResource(asyncResp->res);
320 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000321 else
322 {
Gunnar Millsb8ad5832023-10-02 16:26:07 -0500323 BMCWEB_LOG_ERROR("DBUS response error {}", errorMessage);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000324 messages::internalError(asyncResp->res);
325 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000326}
327
Ed Tanous81ce6092020-12-17 16:54:55 +0000328inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000329 const LDAPConfigData& confData,
330 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530331{
Ed Tanous49cc2632024-03-20 12:49:15 -0700332 nlohmann::json::object_t ldap;
Ed Tanous14766872022-03-15 10:44:42 -0700333 ldap["ServiceEnabled"] = confData.serviceEnabled;
Ed Tanous49cc2632024-03-20 12:49:15 -0700334 nlohmann::json::array_t serviceAddresses;
335 serviceAddresses.emplace_back(confData.uri);
336 ldap["ServiceAddresses"] = std::move(serviceAddresses);
337
338 nlohmann::json::object_t authentication;
339 authentication["AuthenticationType"] =
Ed Tanous0ec8b832022-03-14 14:56:47 -0700340 account_service::AuthenticationTypes::UsernameAndPassword;
Ed Tanous49cc2632024-03-20 12:49:15 -0700341 authentication["Username"] = confData.bindDN;
342 authentication["Password"] = nullptr;
343 ldap["Authentication"] = std::move(authentication);
Ed Tanous14766872022-03-15 10:44:42 -0700344
Ed Tanous49cc2632024-03-20 12:49:15 -0700345 nlohmann::json::object_t ldapService;
346 nlohmann::json::object_t searchSettings;
347 nlohmann::json::array_t baseDistinguishedNames;
348 baseDistinguishedNames.emplace_back(confData.baseDN);
Ed Tanous14766872022-03-15 10:44:42 -0700349
Ed Tanous49cc2632024-03-20 12:49:15 -0700350 searchSettings["BaseDistinguishedNames"] =
351 std::move(baseDistinguishedNames);
352 searchSettings["UsernameAttribute"] = confData.userNameAttribute;
353 searchSettings["GroupsAttribute"] = confData.groupAttribute;
354 ldapService["SearchSettings"] = std::move(searchSettings);
355 ldap["LDAPService"] = std::move(ldapService);
356
357 nlohmann::json::array_t roleMapArray;
Ed Tanous9eb808c2022-01-25 10:19:23 -0800358 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600359 {
Ed Tanous62598e32023-07-17 17:06:25 -0700360 BMCWEB_LOG_DEBUG("Pushing the data groupName={}", obj.second.groupName);
Ed Tanous613dabe2022-07-09 11:17:36 -0700361
Ed Tanous613dabe2022-07-09 11:17:36 -0700362 nlohmann::json::object_t remoteGroup;
363 remoteGroup["RemoteGroup"] = obj.second.groupName;
Jorge Cisneros329f0342022-11-04 16:26:25 +0000364 remoteGroup["LocalRole"] = getRoleIdFromPrivilege(obj.second.privilege);
365 roleMapArray.emplace_back(std::move(remoteGroup));
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600366 }
Ed Tanous49cc2632024-03-20 12:49:15 -0700367
368 ldap["RemoteRoleMapping"] = std::move(roleMapArray);
369
370 jsonResponse[ldapType].update(ldap);
Ratan Gupta6973a582018-12-13 18:25:44 +0530371}
372
373/**
Ratan Gupta06785242019-07-26 22:30:16 +0530374 * @brief validates given JSON input and then calls appropriate method to
375 * create, to delete or to set Rolemapping object based on the given input.
376 *
377 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000378inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800379 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530380 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousc1019822024-03-06 12:54:38 -0800381 const std::string& serverType,
382 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530383{
384 for (size_t index = 0; index < input.size(); index++)
385 {
Ed Tanousc1019822024-03-06 12:54:38 -0800386 std::variant<nlohmann::json::object_t, std::nullptr_t>& thisJson =
387 input[index];
388 nlohmann::json::object_t* obj =
389 std::get_if<nlohmann::json::object_t>(&thisJson);
390 if (obj == nullptr)
Ratan Gupta06785242019-07-26 22:30:16 +0530391 {
392 // delete the existing object
393 if (index < roleMapObjData.size())
394 {
395 crow::connections::systemBus->async_method_call(
396 [asyncResp, roleMapObjData, serverType,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800397 index](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400398 if (ec)
399 {
400 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
401 messages::internalError(asyncResp->res);
402 return;
403 }
404 asyncResp->res
405 .jsonValue[serverType]["RemoteRoleMapping"][index] =
406 nullptr;
407 },
Ratan Gupta06785242019-07-26 22:30:16 +0530408 ldapDbusService, roleMapObjData[index].first,
409 "xyz.openbmc_project.Object.Delete", "Delete");
410 }
411 else
412 {
Ed Tanous62598e32023-07-17 17:06:25 -0700413 BMCWEB_LOG_ERROR("Can't delete the object");
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400414 messages::propertyValueTypeError(
415 asyncResp->res, "null",
416 "RemoteRoleMapping/" + std::to_string(index));
Ratan Gupta06785242019-07-26 22:30:16 +0530417 return;
418 }
419 }
Ed Tanousc1019822024-03-06 12:54:38 -0800420 else if (obj->empty())
Ratan Gupta06785242019-07-26 22:30:16 +0530421 {
422 // Don't do anything for the empty objects,parse next json
423 // eg {"RemoteRoleMapping",[{}]}
424 }
425 else
426 {
427 // update/create the object
428 std::optional<std::string> remoteGroup;
429 std::optional<std::string> localRole;
430
Patrick Williams504af5a2025-02-03 14:29:03 -0500431 if (!json_util::readJsonObject( //
432 *obj, asyncResp->res, //
433 "LocalRole", localRole, //
Myung Baeafc474a2024-10-09 00:53:29 -0700434 "RemoteGroup", remoteGroup //
435 ))
Ratan Gupta06785242019-07-26 22:30:16 +0530436 {
437 continue;
438 }
439
440 // Update existing RoleMapping Object
441 if (index < roleMapObjData.size())
442 {
Ed Tanous62598e32023-07-17 17:06:25 -0700443 BMCWEB_LOG_DEBUG("Update Role Map Object");
Ratan Gupta06785242019-07-26 22:30:16 +0530444 // If "RemoteGroup" info is provided
445 if (remoteGroup)
446 {
Ed Tanousd02aad32024-02-13 14:43:34 -0800447 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530448 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800449 std::format("RemoteRoleMapping/{}/RemoteGroup", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530450 ldapDbusService, roleMapObjData[index].first,
451 "xyz.openbmc_project.User.PrivilegeMapperEntry",
452 "GroupName", *remoteGroup);
Ratan Gupta06785242019-07-26 22:30:16 +0530453 }
454
455 // If "LocalRole" info is provided
456 if (localRole)
457 {
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500458 std::string priv = getPrivilegeFromRoleId(*localRole);
459 if (priv.empty())
460 {
461 messages::propertyValueNotInList(
462 asyncResp->res, *localRole,
463 std::format("RemoteRoleMapping/{}/LocalRole",
464 index));
465 return;
466 }
Ed Tanousd02aad32024-02-13 14:43:34 -0800467 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530468 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800469 std::format("RemoteRoleMapping/{}/LocalRole", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530470 ldapDbusService, roleMapObjData[index].first,
471 "xyz.openbmc_project.User.PrivilegeMapperEntry",
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500472 "Privilege", priv);
Ratan Gupta06785242019-07-26 22:30:16 +0530473 }
474 }
475 // Create a new RoleMapping Object.
476 else
477 {
Ed Tanous62598e32023-07-17 17:06:25 -0700478 BMCWEB_LOG_DEBUG(
479 "setRoleMappingProperties: Creating new Object");
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400480 std::string pathString =
481 "RemoteRoleMapping/" + std::to_string(index);
Ratan Gupta06785242019-07-26 22:30:16 +0530482
483 if (!localRole)
484 {
485 messages::propertyMissing(asyncResp->res,
486 pathString + "/LocalRole");
487 continue;
488 }
489 if (!remoteGroup)
490 {
491 messages::propertyMissing(asyncResp->res,
492 pathString + "/RemoteGroup");
493 continue;
494 }
495
496 std::string dbusObjectPath;
497 if (serverType == "ActiveDirectory")
498 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700499 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530500 }
501 else if (serverType == "LDAP")
502 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000503 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530504 }
505
Ed Tanous62598e32023-07-17 17:06:25 -0700506 BMCWEB_LOG_DEBUG("Remote Group={},LocalRole={}", *remoteGroup,
507 *localRole);
Ratan Gupta06785242019-07-26 22:30:16 +0530508
509 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700510 [asyncResp, serverType, localRole,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800511 remoteGroup](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400512 if (ec)
513 {
514 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
515 messages::internalError(asyncResp->res);
516 return;
517 }
518 nlohmann::json& remoteRoleJson =
519 asyncResp->res
520 .jsonValue[serverType]["RemoteRoleMapping"];
521 nlohmann::json::object_t roleMapEntry;
522 roleMapEntry["LocalRole"] = *localRole;
523 roleMapEntry["RemoteGroup"] = *remoteGroup;
524 remoteRoleJson.emplace_back(std::move(roleMapEntry));
525 },
Ratan Gupta06785242019-07-26 22:30:16 +0530526 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700527 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530528 getPrivilegeFromRoleId(std::move(*localRole)));
529 }
530 }
531 }
532}
533
534/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530535 * Function that retrieves all properties for LDAP config object
536 * into JSON
537 */
538template <typename CallbackFunc>
Patrick Williams504af5a2025-02-03 14:29:03 -0500539inline void getLDAPConfigData(const std::string& ldapType,
540 CallbackFunc&& callback)
Ratan Gupta6973a582018-12-13 18:25:44 +0530541{
George Liu2b731192023-01-11 16:27:13 +0800542 constexpr std::array<std::string_view, 2> interfaces = {
543 ldapEnableInterface, ldapConfigInterface};
Ratan Gupta6973a582018-12-13 18:25:44 +0530544
George Liu2b731192023-01-11 16:27:13 +0800545 dbus::utility::getDbusObject(
546 ldapConfigObjectName, interfaces,
Ed Tanous8cb2c022024-03-27 16:31:46 -0700547 [callback = std::forward<CallbackFunc>(callback),
Ed Tanousc1019822024-03-06 12:54:38 -0800548 ldapType](const boost::system::error_code& ec,
549 const dbus::utility::MapperGetObject& resp) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400550 if (ec || resp.empty())
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600551 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400552 BMCWEB_LOG_WARNING(
553 "DBUS response error during getting of service name: {}",
554 ec);
555 LDAPConfigData empty{};
556 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600557 return;
558 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400559 std::string service = resp.begin()->first;
560 sdbusplus::message::object_path path(ldapRootObject);
561 dbus::utility::getManagedObjects(
562 service, path,
563 [callback, ldapType](const boost::system::error_code& ec2,
564 const dbus::utility::ManagedObjectType&
565 ldapObjects) mutable {
566 LDAPConfigData confData{};
567 if (ec2)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600568 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400569 callback(false, confData, ldapType);
570 BMCWEB_LOG_WARNING("D-Bus responses error: {}", ec2);
571 return;
572 }
573
574 std::string ldapDbusType;
575 std::string searchString;
576
577 if (ldapType == "LDAP")
578 {
579 ldapDbusType =
580 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
581 searchString = "openldap";
582 }
583 else if (ldapType == "ActiveDirectory")
584 {
585 ldapDbusType =
586 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
587 searchString = "active_directory";
588 }
589 else
590 {
591 BMCWEB_LOG_ERROR(
592 "Can't get the DbusType for the given type={}",
593 ldapType);
594 callback(false, confData, ldapType);
595 return;
596 }
597
598 std::string ldapEnableInterfaceStr = ldapEnableInterface;
599 std::string ldapConfigInterfaceStr = ldapConfigInterface;
600
601 for (const auto& object : ldapObjects)
602 {
603 // let's find the object whose ldap type is equal to the
604 // given type
605 if (object.first.str.find(searchString) ==
606 std::string::npos)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600607 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400608 continue;
609 }
610
611 for (const auto& interface : object.second)
612 {
613 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600614 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400615 // rest of the properties are string.
616 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600617 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400618 if (property.first == "Enabled")
619 {
620 const bool* value =
621 std::get_if<bool>(&property.second);
622 if (value == nullptr)
623 {
624 continue;
625 }
626 confData.serviceEnabled = *value;
627 break;
628 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600629 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400630 }
631 else if (interface.first == ldapConfigInterfaceStr)
632 {
633 for (const auto& property : interface.second)
634 {
635 const std::string* strValue =
636 std::get_if<std::string>(
637 &property.second);
638 if (strValue == nullptr)
639 {
640 continue;
641 }
642 if (property.first == "LDAPServerURI")
643 {
644 confData.uri = *strValue;
645 }
646 else if (property.first == "LDAPBindDN")
647 {
648 confData.bindDN = *strValue;
649 }
650 else if (property.first == "LDAPBaseDN")
651 {
652 confData.baseDN = *strValue;
653 }
654 else if (property.first ==
655 "LDAPSearchScope")
656 {
657 confData.searchScope = *strValue;
658 }
659 else if (property.first ==
660 "GroupNameAttribute")
661 {
662 confData.groupAttribute = *strValue;
663 }
664 else if (property.first ==
665 "UserNameAttribute")
666 {
667 confData.userNameAttribute = *strValue;
668 }
669 else if (property.first == "LDAPType")
670 {
671 confData.serverType = *strValue;
672 }
673 }
674 }
675 else if (
676 interface.first ==
677 "xyz.openbmc_project.User.PrivilegeMapperEntry")
678 {
679 LDAPRoleMapData roleMapData{};
680 for (const auto& property : interface.second)
681 {
682 const std::string* strValue =
683 std::get_if<std::string>(
684 &property.second);
685
686 if (strValue == nullptr)
687 {
688 continue;
689 }
690
691 if (property.first == "GroupName")
692 {
693 roleMapData.groupName = *strValue;
694 }
695 else if (property.first == "Privilege")
696 {
697 roleMapData.privilege = *strValue;
698 }
699 }
700
701 confData.groupRoleList.emplace_back(
702 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600703 }
704 }
705 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400706 callback(true, confData, ldapType);
707 });
George Liu2b731192023-01-11 16:27:13 +0800708 });
Ratan Gupta6973a582018-12-13 18:25:44 +0530709}
710
Ed Tanous6c51eab2021-06-03 12:30:29 -0700711/**
Ed Tanous6c51eab2021-06-03 12:30:29 -0700712 * @brief updates the LDAP server address and updates the
713 json response with the new value.
714 * @param serviceAddressList address to be updated.
715 * @param asyncResp pointer to the JSON response
716 * @param ldapServerElementName Type of LDAP
717 server(openLDAP/ActiveDirectory)
718 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530719
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700720inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700721 const std::vector<std::string>& serviceAddressList,
722 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
723 const std::string& ldapServerElementName,
724 const std::string& ldapConfigObject)
725{
Ginu Georgee93abac2024-06-14 17:35:27 +0530726 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceAddress",
727 ldapDbusService, ldapConfigObject, ldapConfigInterface,
728 "LDAPServerURI", serviceAddressList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700729}
730/**
731 * @brief updates the LDAP Bind DN and updates the
732 json response with the new value.
733 * @param username name of the user which needs to be updated.
734 * @param asyncResp pointer to the JSON response
735 * @param ldapServerElementName Type of LDAP
736 server(openLDAP/ActiveDirectory)
737 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530738
Patrick Williams504af5a2025-02-03 14:29:03 -0500739inline void handleUserNamePatch(
740 const std::string& username,
741 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
742 const std::string& ldapServerElementName,
743 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700744{
Ginu Georgee93abac2024-06-14 17:35:27 +0530745 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800746 ldapServerElementName + "/Authentication/Username",
Ginu Georgee93abac2024-06-14 17:35:27 +0530747 ldapDbusService, ldapConfigObject, ldapConfigInterface,
748 "LDAPBindDN", username);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700749}
750
751/**
752 * @brief updates the LDAP password
753 * @param password : ldap password which needs to be updated.
754 * @param asyncResp pointer to the JSON response
755 * @param ldapServerElementName Type of LDAP
756 * server(openLDAP/ActiveDirectory)
757 */
758
Patrick Williams504af5a2025-02-03 14:29:03 -0500759inline void handlePasswordPatch(
760 const std::string& password,
761 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
762 const std::string& ldapServerElementName,
763 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700764{
Ginu Georgee93abac2024-06-14 17:35:27 +0530765 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800766 ldapServerElementName + "/Authentication/Password",
Ginu Georgee93abac2024-06-14 17:35:27 +0530767 ldapDbusService, ldapConfigObject, ldapConfigInterface,
768 "LDAPBindDNPassword", password);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700769}
770
771/**
772 * @brief updates the LDAP BaseDN and updates the
773 json response with the new value.
774 * @param baseDNList baseDN list which needs to be updated.
775 * @param asyncResp pointer to the JSON response
776 * @param ldapServerElementName Type of LDAP
777 server(openLDAP/ActiveDirectory)
778 */
779
Patrick Williams504af5a2025-02-03 14:29:03 -0500780inline void handleBaseDNPatch(
781 const std::vector<std::string>& baseDNList,
782 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
783 const std::string& ldapServerElementName,
784 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700785{
Ginu Georgee93abac2024-06-14 17:35:27 +0530786 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800787 ldapServerElementName +
788 "/LDAPService/SearchSettings/BaseDistinguishedNames",
Ginu Georgee93abac2024-06-14 17:35:27 +0530789 ldapDbusService, ldapConfigObject, ldapConfigInterface,
790 "LDAPBaseDN", baseDNList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700791}
792/**
793 * @brief updates the LDAP user name attribute and updates the
794 json response with the new value.
795 * @param userNameAttribute attribute to be updated.
796 * @param asyncResp pointer to the JSON response
797 * @param ldapServerElementName Type of LDAP
798 server(openLDAP/ActiveDirectory)
799 */
800
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400801inline void handleUserNameAttrPatch(
802 const std::string& userNameAttribute,
803 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
804 const std::string& ldapServerElementName,
805 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700806{
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400807 setDbusProperty(
808 asyncResp,
809 ldapServerElementName + "LDAPService/SearchSettings/UsernameAttribute",
810 ldapDbusService, ldapConfigObject, ldapConfigInterface,
811 "UserNameAttribute", userNameAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700812}
813/**
814 * @brief updates the LDAP group attribute and updates the
815 json response with the new value.
816 * @param groupsAttribute attribute to be updated.
817 * @param asyncResp pointer to the JSON response
818 * @param ldapServerElementName Type of LDAP
819 server(openLDAP/ActiveDirectory)
820 */
821
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700822inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700823 const std::string& groupsAttribute,
824 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
825 const std::string& ldapServerElementName,
826 const std::string& ldapConfigObject)
827{
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400828 setDbusProperty(
829 asyncResp,
830 ldapServerElementName + "/LDAPService/SearchSettings/GroupsAttribute",
831 ldapDbusService, ldapConfigObject, ldapConfigInterface,
832 "GroupNameAttribute", groupsAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700833}
834/**
835 * @brief updates the LDAP service enable and updates the
836 json response with the new value.
837 * @param input JSON data.
838 * @param asyncResp pointer to the JSON response
839 * @param ldapServerElementName Type of LDAP
840 server(openLDAP/ActiveDirectory)
841 */
842
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700843inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700844 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
845 const std::string& ldapServerElementName,
846 const std::string& ldapConfigObject)
847{
Ginu Georgee93abac2024-06-14 17:35:27 +0530848 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceEnabled",
849 ldapDbusService, ldapConfigObject, ldapEnableInterface,
850 "Enabled", serviceEnabled);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700851}
852
Ed Tanousc1019822024-03-06 12:54:38 -0800853struct AuthMethods
Ed Tanous6c51eab2021-06-03 12:30:29 -0700854{
855 std::optional<bool> basicAuth;
856 std::optional<bool> cookie;
857 std::optional<bool> sessionToken;
858 std::optional<bool> xToken;
859 std::optional<bool> tls;
Ed Tanousc1019822024-03-06 12:54:38 -0800860};
Ed Tanous6c51eab2021-06-03 12:30:29 -0700861
Patrick Williams504af5a2025-02-03 14:29:03 -0500862inline void handleAuthMethodsPatch(
863 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
864 const AuthMethods& auth)
Ed Tanousc1019822024-03-06 12:54:38 -0800865{
866 persistent_data::AuthConfigMethods& authMethodsConfig =
Ed Tanous6c51eab2021-06-03 12:30:29 -0700867 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
868
Ed Tanousc1019822024-03-06 12:54:38 -0800869 if (auth.basicAuth)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700870 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700871 if constexpr (!BMCWEB_BASIC_AUTH)
872 {
873 messages::actionNotSupported(
874 asyncResp->res,
875 "Setting BasicAuth when basic-auth feature is disabled");
876 return;
877 }
878
Ed Tanousc1019822024-03-06 12:54:38 -0800879 authMethodsConfig.basic = *auth.basicAuth;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700880 }
881
Ed Tanousc1019822024-03-06 12:54:38 -0800882 if (auth.cookie)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700883 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700884 if constexpr (!BMCWEB_COOKIE_AUTH)
885 {
886 messages::actionNotSupported(
887 asyncResp->res,
888 "Setting Cookie when cookie-auth feature is disabled");
889 return;
890 }
Ed Tanousc1019822024-03-06 12:54:38 -0800891 authMethodsConfig.cookie = *auth.cookie;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700892 }
893
Ed Tanousc1019822024-03-06 12:54:38 -0800894 if (auth.sessionToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700895 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700896 if constexpr (!BMCWEB_SESSION_AUTH)
897 {
898 messages::actionNotSupported(
899 asyncResp->res,
900 "Setting SessionToken when session-auth feature is disabled");
901 return;
902 }
Ed Tanousc1019822024-03-06 12:54:38 -0800903 authMethodsConfig.sessionToken = *auth.sessionToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700904 }
905
Ed Tanousc1019822024-03-06 12:54:38 -0800906 if (auth.xToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700907 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700908 if constexpr (!BMCWEB_XTOKEN_AUTH)
909 {
910 messages::actionNotSupported(
911 asyncResp->res,
912 "Setting XToken when xtoken-auth feature is disabled");
913 return;
914 }
Ed Tanousc1019822024-03-06 12:54:38 -0800915 authMethodsConfig.xtoken = *auth.xToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700916 }
917
Ed Tanousc1019822024-03-06 12:54:38 -0800918 if (auth.tls)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700919 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700920 if constexpr (!BMCWEB_MUTUAL_TLS_AUTH)
921 {
922 messages::actionNotSupported(
923 asyncResp->res,
924 "Setting TLS when mutual-tls-auth feature is disabled");
925 return;
926 }
Ed Tanousc1019822024-03-06 12:54:38 -0800927 authMethodsConfig.tls = *auth.tls;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700928 }
929
930 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
931 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
932 !authMethodsConfig.tls)
933 {
934 // Do not allow user to disable everything
935 messages::actionNotSupported(asyncResp->res,
936 "of disabling all available methods");
937 return;
938 }
939
940 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
941 authMethodsConfig);
942 // Save configuration immediately
943 persistent_data::getConfig().writeData();
944
945 messages::success(asyncResp->res);
946}
947
948/**
949 * @brief Get the required values from the given JSON, validates the
950 * value and create the LDAP config object.
951 * @param input JSON data
952 * @param asyncResp pointer to the JSON response
953 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
954 */
955
Ed Tanous10cb44f2024-04-11 13:05:20 -0700956struct LdapPatchParams
957{
958 std::optional<std::string> authType;
959 std::optional<std::vector<std::string>> serviceAddressList;
960 std::optional<bool> serviceEnabled;
961 std::optional<std::vector<std::string>> baseDNList;
962 std::optional<std::string> userNameAttribute;
963 std::optional<std::string> groupsAttribute;
964 std::optional<std::string> userName;
965 std::optional<std::string> password;
966 std::optional<
967 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>
968 remoteRoleMapData;
969};
970
971inline void handleLDAPPatch(LdapPatchParams&& input,
Ed Tanous6c51eab2021-06-03 12:30:29 -0700972 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
973 const std::string& serverType)
974{
975 std::string dbusObjectPath;
976 if (serverType == "ActiveDirectory")
977 {
978 dbusObjectPath = adConfigObject;
979 }
980 else if (serverType == "LDAP")
981 {
982 dbusObjectPath = ldapConfigObjectName;
983 }
984 else
985 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700986 BMCWEB_LOG_ERROR("serverType wasn't AD or LDAP but was {}????",
987 serverType);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700988 return;
989 }
990
Ed Tanous10cb44f2024-04-11 13:05:20 -0700991 if (input.authType && *input.authType != "UsernameAndPassword")
Ed Tanous6c51eab2021-06-03 12:30:29 -0700992 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700993 messages::propertyValueNotInList(asyncResp->res, *input.authType,
Ed Tanousc1019822024-03-06 12:54:38 -0800994 "AuthenticationType");
995 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700996 }
Ed Tanousc1019822024-03-06 12:54:38 -0800997
Ed Tanous10cb44f2024-04-11 13:05:20 -0700998 if (input.serviceAddressList)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700999 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001000 if (input.serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301001 {
Ed Tanouse2616cc2022-06-27 12:45:55 -07001002 messages::propertyValueNotInList(
Ed Tanous10cb44f2024-04-11 13:05:20 -07001003 asyncResp->res, *input.serviceAddressList, "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001004 return;
1005 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001006 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001007 if (input.baseDNList)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001008 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001009 if (input.baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +05301010 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001011 messages::propertyValueNotInList(asyncResp->res, *input.baseDNList,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001012 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301013 return;
1014 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001015 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301016
Ed Tanous6c51eab2021-06-03 12:30:29 -07001017 // nothing to update, then return
Ed Tanous10cb44f2024-04-11 13:05:20 -07001018 if (!input.userName && !input.password && !input.serviceAddressList &&
1019 !input.baseDNList && !input.userNameAttribute &&
1020 !input.groupsAttribute && !input.serviceEnabled &&
1021 !input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001022 {
1023 return;
1024 }
1025
1026 // Get the existing resource first then keep modifying
1027 // whenever any property gets updated.
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001028 getLDAPConfigData(serverType, [asyncResp, input = std::move(input),
1029 dbusObjectPath = std::move(dbusObjectPath)](
1030 bool success,
1031 const LDAPConfigData& confData,
1032 const std::string& serverT) mutable {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001033 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301034 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001035 messages::internalError(asyncResp->res);
1036 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301037 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001038 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1039 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301040 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001041 // Disable the service first and update the rest of
1042 // the properties.
1043 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301044 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001045
Ed Tanous10cb44f2024-04-11 13:05:20 -07001046 if (input.serviceAddressList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301047 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001048 handleServiceAddressPatch(*input.serviceAddressList, asyncResp,
1049 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301050 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001051 if (input.userName)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001052 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001053 handleUserNamePatch(*input.userName, asyncResp, serverT,
1054 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001055 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001056 if (input.password)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001057 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001058 handlePasswordPatch(*input.password, asyncResp, serverT,
1059 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001060 }
1061
Ed Tanous10cb44f2024-04-11 13:05:20 -07001062 if (input.baseDNList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301063 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001064 handleBaseDNPatch(*input.baseDNList, asyncResp, serverT,
1065 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001066 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001067 if (input.userNameAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001068 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001069 handleUserNameAttrPatch(*input.userNameAttribute, asyncResp,
1070 serverT, dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001071 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001072 if (input.groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001073 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001074 handleGroupNameAttrPatch(*input.groupsAttribute, asyncResp, serverT,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001075 dbusObjectPath);
1076 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001077 if (input.serviceEnabled)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001078 {
1079 // if user has given the value as true then enable
1080 // the service. if user has given false then no-op
1081 // as service is already stopped.
Ed Tanous10cb44f2024-04-11 13:05:20 -07001082 if (*input.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301083 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001084 handleServiceEnablePatch(*input.serviceEnabled, asyncResp,
1085 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301086 }
1087 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001088 else
1089 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001090 // if user has not given the service enabled value
1091 // then revert it to the same state as it was
1092 // before.
1093 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1094 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001095 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001096
Ed Tanous10cb44f2024-04-11 13:05:20 -07001097 if (input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001098 {
1099 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001100 *input.remoteRoleMapData);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001101 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001102 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001103}
1104
Ed Tanous492ec932024-12-09 15:42:19 -08001105struct UserUpdateParams
1106{
1107 std::string username;
1108 std::optional<std::string> password;
1109 std::optional<bool> enabled;
1110 std::optional<std::string> roleId;
1111 std::optional<bool> locked;
1112 std::optional<std::vector<std::string>> accountTypes;
1113 bool userSelf;
1114 std::shared_ptr<persistent_data::UserSession> session;
1115 std::string dbusObjectPath;
1116};
1117
Patrick Williams504af5a2025-02-03 14:29:03 -05001118inline void afterVerifyUserExists(
1119 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1120 const UserUpdateParams& params, int rc)
Ed Tanous492ec932024-12-09 15:42:19 -08001121{
1122 if (rc <= 0)
1123 {
1124 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1125 params.username);
1126 return;
1127 }
1128
1129 if (params.password)
1130 {
1131 int retval = pamUpdatePassword(params.username, *params.password);
1132
1133 if (retval == PAM_USER_UNKNOWN)
1134 {
1135 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1136 params.username);
1137 }
1138 else if (retval == PAM_AUTHTOK_ERR)
1139 {
1140 // If password is invalid
1141 messages::propertyValueFormatError(asyncResp->res, nullptr,
1142 "Password");
1143 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
1144 }
1145 else if (retval != PAM_SUCCESS)
1146 {
1147 messages::internalError(asyncResp->res);
1148 return;
1149 }
1150 else
1151 {
1152 // Remove existing sessions of the user when password
1153 // changed
1154 persistent_data::SessionStore::getInstance()
1155 .removeSessionsByUsernameExceptSession(params.username,
1156 params.session);
1157 messages::success(asyncResp->res);
1158 }
1159 }
1160
1161 if (params.enabled)
1162 {
1163 setDbusProperty(
1164 asyncResp, "Enabled", "xyz.openbmc_project.User.Manager",
1165 params.dbusObjectPath, "xyz.openbmc_project.User.Attributes",
1166 "UserEnabled", *params.enabled);
1167 }
1168
1169 if (params.roleId)
1170 {
1171 std::string priv = getPrivilegeFromRoleId(*params.roleId);
1172 if (priv.empty())
1173 {
1174 messages::propertyValueNotInList(asyncResp->res, true, "Locked");
1175 return;
1176 }
1177 setDbusProperty(asyncResp, "RoleId", "xyz.openbmc_project.User.Manager",
1178 params.dbusObjectPath,
1179 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1180 priv);
1181 }
1182
1183 if (params.locked)
1184 {
1185 // admin can unlock the account which is locked by
1186 // successive authentication failures but admin should
1187 // not be allowed to lock an account.
1188 if (*params.locked)
1189 {
1190 messages::propertyValueNotInList(asyncResp->res, "true", "Locked");
1191 return;
1192 }
1193 setDbusProperty(asyncResp, "Locked", "xyz.openbmc_project.User.Manager",
1194 params.dbusObjectPath,
1195 "xyz.openbmc_project.User.Attributes",
1196 "UserLockedForFailedAttempt", *params.locked);
1197 }
1198
1199 if (params.accountTypes)
1200 {
1201 patchAccountTypes(*params.accountTypes, asyncResp,
1202 params.dbusObjectPath, params.userSelf);
1203 }
1204}
1205
Abhishek Patel58345852022-02-02 08:54:25 -06001206inline void updateUserProperties(
Ed Tanous492ec932024-12-09 15:42:19 -08001207 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1208 const std::string& username, const std::optional<std::string>& password,
Abhishek Patel58345852022-02-02 08:54:25 -06001209 const std::optional<bool>& enabled,
1210 const std::optional<std::string>& roleId, const std::optional<bool>& locked,
Ed Tanous492ec932024-12-09 15:42:19 -08001211 const std::optional<std::vector<std::string>>& accountTypes, bool userSelf,
Ravi Tejae518ef32024-05-16 10:33:08 -05001212 const std::shared_ptr<persistent_data::UserSession>& session)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001213{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301214 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1215 tempObjPath /= username;
1216 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001217
Ed Tanous492ec932024-12-09 15:42:19 -08001218 UserUpdateParams params{username, password, enabled,
1219 roleId, locked, accountTypes,
1220 userSelf, session, dbusObjectPath};
1221
Ed Tanous6c51eab2021-06-03 12:30:29 -07001222 dbus::utility::checkDbusPathExists(
Ravi Tejae518ef32024-05-16 10:33:08 -05001223 dbusObjectPath,
Ed Tanous492ec932024-12-09 15:42:19 -08001224 std::bind_front(afterVerifyUserExists, asyncResp, std::move(params)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001225}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001226
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001227inline void handleAccountServiceHead(
1228 App& app, const crow::Request& req,
1229 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001230{
1231 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1232 {
1233 return;
1234 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001235 asyncResp->res.addHeader(
1236 boost::beast::http::field::link,
1237 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1238}
1239
Patrick Williams504af5a2025-02-03 14:29:03 -05001240inline void getClientCertificates(
1241 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1242 const nlohmann::json::json_pointer& keyLocation)
Ed Tanous1aa375b2024-04-13 11:51:10 -07001243{
1244 boost::urls::url url(
1245 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates");
1246 std::array<std::string_view, 1> interfaces = {
1247 "xyz.openbmc_project.Certs.Certificate"};
1248 std::string path = "/xyz/openbmc_project/certs/authority/truststore";
1249
1250 collection_util::getCollectionToKey(asyncResp, url, interfaces, path,
1251 keyLocation);
1252}
1253
1254inline void handleAccountServiceClientCertificatesInstanceHead(
1255 App& app, const crow::Request& req,
1256 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1257 const std::string& /*id*/)
1258{
1259 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1260 {
1261 return;
1262 }
1263
1264 asyncResp->res.addHeader(
1265 boost::beast::http::field::link,
1266 "</redfish/v1/JsonSchemas/Certificate/Certificate.json>; rel=describedby");
1267}
1268
1269inline void handleAccountServiceClientCertificatesInstanceGet(
1270 App& app, const crow::Request& req,
1271 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1272{
1273 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1274 {
1275 return;
1276 }
1277 BMCWEB_LOG_DEBUG("ClientCertificate Certificate ID={}", id);
1278 const boost::urls::url certURL = boost::urls::format(
1279 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/{}",
1280 id);
1281 std::string objPath =
1282 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1283 getCertificateProperties(
1284 asyncResp, objPath,
1285 "xyz.openbmc_project.Certs.Manager.Authority.Truststore", id, certURL,
1286 "Client Certificate");
1287}
1288
1289inline void handleAccountServiceClientCertificatesHead(
1290 App& app, const crow::Request& req,
1291 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1292{
1293 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1294 {
1295 return;
1296 }
1297
1298 asyncResp->res.addHeader(
1299 boost::beast::http::field::link,
1300 "</redfish/v1/JsonSchemas/CertificateCollection/CertificateCollection.json>; rel=describedby");
1301}
1302
1303inline void handleAccountServiceClientCertificatesGet(
1304 App& app, const crow::Request& req,
1305 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1306{
1307 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1308 {
1309 return;
1310 }
Myung Bae0f09ed32025-03-31 15:32:24 -05001311
1312 nlohmann::json& json = asyncResp->res.jsonValue;
1313 json["@odata.id"] =
1314 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates";
1315 json["@odata.type"] = "#CertificateCollection.CertificateCollection";
1316 json["Name"] = "Certificates Collection";
1317 json["Description"] = "Multi-factor Authentication Client Certificates";
Ed Tanous1aa375b2024-04-13 11:51:10 -07001318 getClientCertificates(asyncResp, "/Members"_json_pointer);
1319}
1320
Ed Tanous3ce36882024-06-09 10:58:16 -07001321using account_service::CertificateMappingAttribute;
1322using persistent_data::MTLSCommonNameParseMode;
Patrick Williams504af5a2025-02-03 14:29:03 -05001323inline CertificateMappingAttribute getCertificateMapping(
1324 MTLSCommonNameParseMode parse)
Ed Tanous3ce36882024-06-09 10:58:16 -07001325{
1326 switch (parse)
1327 {
1328 case MTLSCommonNameParseMode::CommonName:
1329 {
1330 return CertificateMappingAttribute::CommonName;
1331 }
1332 break;
1333 case MTLSCommonNameParseMode::Whole:
1334 {
1335 return CertificateMappingAttribute::Whole;
1336 }
1337 break;
1338 case MTLSCommonNameParseMode::UserPrincipalName:
1339 {
1340 return CertificateMappingAttribute::UserPrincipalName;
1341 }
1342 break;
1343
1344 case MTLSCommonNameParseMode::Meta:
1345 {
1346 if constexpr (BMCWEB_META_TLS_COMMON_NAME_PARSING)
1347 {
1348 return CertificateMappingAttribute::CommonName;
1349 }
1350 }
1351 break;
1352 default:
1353 {
1354 return CertificateMappingAttribute::Invalid;
1355 }
1356 break;
1357 }
1358}
1359
Patrick Williams504af5a2025-02-03 14:29:03 -05001360inline void handleAccountServiceGet(
1361 App& app, const crow::Request& req,
1362 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001363{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001364 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1365 {
1366 return;
1367 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001368
1369 if (req.session == nullptr)
1370 {
1371 messages::internalError(asyncResp->res);
1372 return;
1373 }
1374
Ed Tanousc1019822024-03-06 12:54:38 -08001375 const persistent_data::AuthConfigMethods& authMethodsConfig =
1376 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1377
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001378 asyncResp->res.addHeader(
1379 boost::beast::http::field::link,
1380 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1381
Ed Tanous1ef4c342022-05-12 16:12:36 -07001382 nlohmann::json& json = asyncResp->res.jsonValue;
1383 json["@odata.id"] = "/redfish/v1/AccountService";
Ravi Teja482a69e2024-04-22 06:56:13 -05001384 json["@odata.type"] = "#AccountService.v1_15_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001385 json["Id"] = "AccountService";
1386 json["Name"] = "Account Service";
1387 json["Description"] = "Account Service";
1388 json["ServiceEnabled"] = true;
1389 json["MaxPasswordLength"] = 20;
1390 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1391 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
Ravi Teja482a69e2024-04-22 06:56:13 -05001392 json["HTTPBasicAuth"] = authMethodsConfig.basic
1393 ? account_service::BasicAuthState::Enabled
1394 : account_service::BasicAuthState::Disabled;
1395
1396 nlohmann::json::array_t allowed;
1397 allowed.emplace_back(account_service::BasicAuthState::Enabled);
1398 allowed.emplace_back(account_service::BasicAuthState::Disabled);
1399 json["HTTPBasicAuth@AllowableValues"] = std::move(allowed);
1400
Ed Tanous1aa375b2024-04-13 11:51:10 -07001401 nlohmann::json::object_t clientCertificate;
1402 clientCertificate["Enabled"] = authMethodsConfig.tls;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001403 clientCertificate["RespondToUnauthenticatedClients"] =
1404 !authMethodsConfig.tlsStrict;
Ed Tanous3ce36882024-06-09 10:58:16 -07001405
1406 using account_service::CertificateMappingAttribute;
1407
1408 CertificateMappingAttribute mapping =
1409 getCertificateMapping(authMethodsConfig.mTLSCommonNameParsingMode);
1410 if (mapping == CertificateMappingAttribute::Invalid)
1411 {
1412 messages::internalError(asyncResp->res);
1413 }
1414 else
1415 {
1416 clientCertificate["CertificateMappingAttribute"] = mapping;
1417 }
Ed Tanous1aa375b2024-04-13 11:51:10 -07001418 nlohmann::json::object_t certificates;
1419 certificates["@odata.id"] =
1420 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates";
1421 certificates["@odata.type"] =
1422 "#CertificateCollection.CertificateCollection";
1423 clientCertificate["Certificates"] = std::move(certificates);
1424 json["MultiFactorAuth"]["ClientCertificate"] = std::move(clientCertificate);
1425
1426 getClientCertificates(
1427 asyncResp,
1428 "/MultiFactorAuth/ClientCertificate/Certificates/Members"_json_pointer);
1429
Ed Tanous1ef4c342022-05-12 16:12:36 -07001430 json["Oem"]["OpenBMC"]["@odata.type"] =
Ed Tanous5b5574a2022-09-26 19:53:36 -07001431 "#OpenBMCAccountService.v1_0_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001432 json["Oem"]["OpenBMC"]["@odata.id"] =
1433 "/redfish/v1/AccountService#/Oem/OpenBMC";
1434 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1435 authMethodsConfig.basic;
1436 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1437 authMethodsConfig.sessionToken;
1438 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1439 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1440 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1441
1442 // /redfish/v1/AccountService/LDAP/Certificates is something only
1443 // ConfigureManager can access then only display when the user has
1444 // permissions ConfigureManager
1445 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001446 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001447
1448 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1449 effectiveUserPrivileges))
1450 {
1451 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1452 "/redfish/v1/AccountService/LDAP/Certificates";
1453 }
Ed Tanousdeae6a72024-11-11 21:58:57 -08001454 dbus::utility::getAllProperties(
1455 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1456 "xyz.openbmc_project.User.AccountPolicy",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001457 [asyncResp](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001458 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001459 if (ec)
1460 {
1461 messages::internalError(asyncResp->res);
1462 return;
1463 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001464
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001465 BMCWEB_LOG_DEBUG("Got {} properties for AccountService",
1466 propertiesList.size());
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001467
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001468 const uint8_t* minPasswordLength = nullptr;
1469 const uint32_t* accountUnlockTimeout = nullptr;
1470 const uint16_t* maxLoginAttemptBeforeLockout = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001471
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001472 const bool success = sdbusplus::unpackPropertiesNoThrow(
1473 dbus_utils::UnpackErrorPrinter(), propertiesList,
1474 "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout",
1475 accountUnlockTimeout, "MaxLoginAttemptBeforeLockout",
1476 maxLoginAttemptBeforeLockout);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001477
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001478 if (!success)
1479 {
1480 messages::internalError(asyncResp->res);
1481 return;
1482 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001483
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001484 if (minPasswordLength != nullptr)
1485 {
1486 asyncResp->res.jsonValue["MinPasswordLength"] =
1487 *minPasswordLength;
1488 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001489
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001490 if (accountUnlockTimeout != nullptr)
1491 {
1492 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1493 *accountUnlockTimeout;
1494 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001495
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001496 if (maxLoginAttemptBeforeLockout != nullptr)
1497 {
1498 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1499 *maxLoginAttemptBeforeLockout;
1500 }
1501 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001502
Ed Tanous02cad962022-06-30 16:50:15 -07001503 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001504 const std::string& ldapType) {
1505 if (!success)
1506 {
1507 return;
1508 }
1509 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1510 };
1511
1512 getLDAPConfigData("LDAP", callback);
1513 getLDAPConfigData("ActiveDirectory", callback);
1514}
1515
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001516inline void handleCertificateMappingAttributePatch(
1517 crow::Response& res, const std::string& certMapAttribute)
Ed Tanous3ce36882024-06-09 10:58:16 -07001518{
1519 MTLSCommonNameParseMode parseMode =
1520 persistent_data::getMTLSCommonNameParseMode(certMapAttribute);
1521 if (parseMode == MTLSCommonNameParseMode::Invalid)
1522 {
1523 messages::propertyValueNotInList(res, "CertificateMappingAttribute",
1524 certMapAttribute);
1525 return;
1526 }
1527
1528 persistent_data::AuthConfigMethods& authMethodsConfig =
1529 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1530 authMethodsConfig.mTLSCommonNameParsingMode = parseMode;
1531}
1532
Ed Tanous3281bcf2024-06-25 16:02:05 -07001533inline void handleRespondToUnauthenticatedClientsPatch(
1534 App& app, const crow::Request& req, crow::Response& res,
1535 bool respondToUnauthenticatedClients)
1536{
1537 if (req.session != nullptr)
1538 {
1539 // Sanity check. If the user isn't currently authenticated with mutual
1540 // TLS, they very likely are about to permanently lock themselves out.
1541 // Make sure they're using mutual TLS before allowing locking.
1542 if (req.session->sessionType != persistent_data::SessionType::MutualTLS)
1543 {
1544 messages::propertyValueExternalConflict(
1545 res,
1546 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
1547 respondToUnauthenticatedClients);
1548 return;
1549 }
1550 }
1551
1552 persistent_data::AuthConfigMethods& authMethodsConfig =
1553 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1554
1555 // Change the settings
1556 authMethodsConfig.tlsStrict = !respondToUnauthenticatedClients;
1557
1558 // Write settings to disk
1559 persistent_data::getConfig().writeData();
1560
1561 // Trigger a reload, to apply the new settings to new connections
1562 app.loadCertificate();
1563}
1564
Ed Tanous1ef4c342022-05-12 16:12:36 -07001565inline void handleAccountServicePatch(
1566 App& app, const crow::Request& req,
1567 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1568{
1569 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1570 {
1571 return;
1572 }
1573 std::optional<uint32_t> unlockTimeout;
1574 std::optional<uint16_t> lockoutThreshold;
1575 std::optional<uint8_t> minPasswordLength;
1576 std::optional<uint16_t> maxPasswordLength;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001577 LdapPatchParams ldapObject;
Ed Tanous3ce36882024-06-09 10:58:16 -07001578 std::optional<std::string> certificateMappingAttribute;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001579 std::optional<bool> respondToUnauthenticatedClients;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001580 LdapPatchParams activeDirectoryObject;
Ed Tanousc1019822024-03-06 12:54:38 -08001581 AuthMethods auth;
Ravi Teja482a69e2024-04-22 06:56:13 -05001582 std::optional<std::string> httpBasicAuth;
Ed Tanous3ce36882024-06-09 10:58:16 -07001583
Patrick Williams504af5a2025-02-03 14:29:03 -05001584 if (!json_util::readJsonPatch( //
1585 req, asyncResp->res, //
1586 "AccountLockoutDuration", unlockTimeout, //
1587 "AccountLockoutThreshold", lockoutThreshold, //
Myung Baeafc474a2024-10-09 00:53:29 -07001588 "ActiveDirectory/Authentication/AuthenticationType",
Patrick Williams504af5a2025-02-03 14:29:03 -05001589 activeDirectoryObject.authType, //
Myung Baeafc474a2024-10-09 00:53:29 -07001590 "ActiveDirectory/Authentication/Password",
Patrick Williams504af5a2025-02-03 14:29:03 -05001591 activeDirectoryObject.password, //
Myung Baeafc474a2024-10-09 00:53:29 -07001592 "ActiveDirectory/Authentication/Username",
Patrick Williams504af5a2025-02-03 14:29:03 -05001593 activeDirectoryObject.userName, //
Myung Baeafc474a2024-10-09 00:53:29 -07001594 "ActiveDirectory/LDAPService/SearchSettings/BaseDistinguishedNames",
Patrick Williams504af5a2025-02-03 14:29:03 -05001595 activeDirectoryObject.baseDNList, //
Myung Baeafc474a2024-10-09 00:53:29 -07001596 "ActiveDirectory/LDAPService/SearchSettings/GroupsAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001597 activeDirectoryObject.groupsAttribute, //
Myung Baeafc474a2024-10-09 00:53:29 -07001598 "ActiveDirectory/LDAPService/SearchSettings/UsernameAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001599 activeDirectoryObject.userNameAttribute, //
Myung Baeafc474a2024-10-09 00:53:29 -07001600 "ActiveDirectory/RemoteRoleMapping",
Patrick Williams504af5a2025-02-03 14:29:03 -05001601 activeDirectoryObject.remoteRoleMapData, //
Myung Baeafc474a2024-10-09 00:53:29 -07001602 "ActiveDirectory/ServiceAddresses",
Patrick Williams504af5a2025-02-03 14:29:03 -05001603 activeDirectoryObject.serviceAddressList, //
Myung Baeafc474a2024-10-09 00:53:29 -07001604 "ActiveDirectory/ServiceEnabled",
Patrick Williams504af5a2025-02-03 14:29:03 -05001605 activeDirectoryObject.serviceEnabled, //
1606 "HTTPBasicAuth", httpBasicAuth, //
Myung Baeafc474a2024-10-09 00:53:29 -07001607 "LDAP/Authentication/AuthenticationType", ldapObject.authType, //
Patrick Williams504af5a2025-02-03 14:29:03 -05001608 "LDAP/Authentication/Password", ldapObject.password, //
1609 "LDAP/Authentication/Username", ldapObject.userName, //
Myung Baeafc474a2024-10-09 00:53:29 -07001610 "LDAP/LDAPService/SearchSettings/BaseDistinguishedNames",
Patrick Williams504af5a2025-02-03 14:29:03 -05001611 ldapObject.baseDNList, //
Myung Baeafc474a2024-10-09 00:53:29 -07001612 "LDAP/LDAPService/SearchSettings/GroupsAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001613 ldapObject.groupsAttribute, //
Myung Baeafc474a2024-10-09 00:53:29 -07001614 "LDAP/LDAPService/SearchSettings/UsernameAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001615 ldapObject.userNameAttribute, //
1616 "LDAP/RemoteRoleMapping", ldapObject.remoteRoleMapData, //
1617 "LDAP/ServiceAddresses", ldapObject.serviceAddressList, //
1618 "LDAP/ServiceEnabled", ldapObject.serviceEnabled, //
1619 "MaxPasswordLength", maxPasswordLength, //
1620 "MinPasswordLength", minPasswordLength, //
Myung Baeafc474a2024-10-09 00:53:29 -07001621 "MultiFactorAuth/ClientCertificate/CertificateMappingAttribute",
Patrick Williams504af5a2025-02-03 14:29:03 -05001622 certificateMappingAttribute, //
Myung Baeafc474a2024-10-09 00:53:29 -07001623 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
Patrick Williams504af5a2025-02-03 14:29:03 -05001624 respondToUnauthenticatedClients, //
1625 "Oem/OpenBMC/AuthMethods/BasicAuth", auth.basicAuth, //
1626 "Oem/OpenBMC/AuthMethods/Cookie", auth.cookie, //
1627 "Oem/OpenBMC/AuthMethods/SessionToken", auth.sessionToken, //
1628 "Oem/OpenBMC/AuthMethods/TLS", auth.tls, //
1629 "Oem/OpenBMC/AuthMethods/XToken", auth.xToken //
Myung Baeafc474a2024-10-09 00:53:29 -07001630 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001631 {
1632 return;
1633 }
1634
Ravi Teja482a69e2024-04-22 06:56:13 -05001635 if (httpBasicAuth)
1636 {
1637 if (*httpBasicAuth == "Enabled")
1638 {
1639 auth.basicAuth = true;
1640 }
1641 else if (*httpBasicAuth == "Disabled")
1642 {
1643 auth.basicAuth = false;
1644 }
1645 else
1646 {
1647 messages::propertyValueNotInList(asyncResp->res, "HttpBasicAuth",
1648 *httpBasicAuth);
1649 }
1650 }
1651
Ed Tanous3281bcf2024-06-25 16:02:05 -07001652 if (respondToUnauthenticatedClients)
1653 {
1654 handleRespondToUnauthenticatedClientsPatch(
1655 app, req, asyncResp->res, *respondToUnauthenticatedClients);
1656 }
1657
Ed Tanous3ce36882024-06-09 10:58:16 -07001658 if (certificateMappingAttribute)
1659 {
1660 handleCertificateMappingAttributePatch(asyncResp->res,
1661 *certificateMappingAttribute);
1662 }
1663
Ed Tanous1ef4c342022-05-12 16:12:36 -07001664 if (minPasswordLength)
1665 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001666 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301667 asyncResp, "MinPasswordLength", "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001668 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001669 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
Ginu Georgee93abac2024-06-14 17:35:27 +05301670 *minPasswordLength);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001671 }
1672
1673 if (maxPasswordLength)
1674 {
1675 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1676 }
1677
Ed Tanous10cb44f2024-04-11 13:05:20 -07001678 handleLDAPPatch(std::move(activeDirectoryObject), asyncResp,
1679 "ActiveDirectory");
1680 handleLDAPPatch(std::move(ldapObject), asyncResp, "LDAP");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001681
Ed Tanousc1019822024-03-06 12:54:38 -08001682 handleAuthMethodsPatch(asyncResp, auth);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001683
Ed Tanous1ef4c342022-05-12 16:12:36 -07001684 if (unlockTimeout)
1685 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001686 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301687 asyncResp, "AccountLockoutDuration",
1688 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001689 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
Ed Tanous1ef4c342022-05-12 16:12:36 -07001690 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
Ginu Georgee93abac2024-06-14 17:35:27 +05301691 *unlockTimeout);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001692 }
1693 if (lockoutThreshold)
1694 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001695 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301696 asyncResp, "AccountLockoutThreshold",
1697 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001698 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001699 "xyz.openbmc_project.User.AccountPolicy",
Ginu Georgee93abac2024-06-14 17:35:27 +05301700 "MaxLoginAttemptBeforeLockout", *lockoutThreshold);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001701 }
1702}
1703
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001704inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001705 App& app, const crow::Request& req,
1706 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1707{
1708 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1709 {
1710 return;
1711 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001712 asyncResp->res.addHeader(
1713 boost::beast::http::field::link,
1714 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1715}
1716
1717inline void handleAccountCollectionGet(
1718 App& app, const crow::Request& req,
1719 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1720{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001721 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1722 {
1723 return;
1724 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001725
1726 if (req.session == nullptr)
1727 {
1728 messages::internalError(asyncResp->res);
1729 return;
1730 }
1731
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001732 asyncResp->res.addHeader(
1733 boost::beast::http::field::link,
1734 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001735
1736 asyncResp->res.jsonValue["@odata.id"] =
1737 "/redfish/v1/AccountService/Accounts";
1738 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1739 "ManagerAccountCollection";
1740 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1741 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1742
1743 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001744 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001745
1746 std::string thisUser;
1747 if (req.session)
1748 {
1749 thisUser = req.session->username;
1750 }
George Liu5eb468d2023-06-20 17:03:24 +08001751 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
1752 dbus::utility::getManagedObjects(
1753 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001754 [asyncResp, thisUser, effectiveUserPrivileges](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001755 const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001756 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001757 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001758 {
1759 messages::internalError(asyncResp->res);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001760 return;
1761 }
1762
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001763 bool userCanSeeAllAccounts =
1764 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1765
1766 bool userCanSeeSelf =
1767 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1768
1769 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1770 memberArray = nlohmann::json::array();
1771
1772 for (const auto& userpath : users)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001773 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001774 std::string user = userpath.first.filename();
1775 if (user.empty())
1776 {
1777 messages::internalError(asyncResp->res);
1778 BMCWEB_LOG_ERROR("Invalid firmware ID");
1779
1780 return;
1781 }
1782
1783 // As clarified by Redfish here:
1784 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1785 // Users without ConfigureUsers, only see their own
1786 // account. Users with ConfigureUsers, see all
1787 // accounts.
1788 if (userCanSeeAllAccounts ||
1789 (thisUser == user && userCanSeeSelf))
1790 {
1791 nlohmann::json::object_t member;
1792 member["@odata.id"] = boost::urls::format(
1793 "/redfish/v1/AccountService/Accounts/{}", user);
1794 memberArray.emplace_back(std::move(member));
1795 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001796 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001797 asyncResp->res.jsonValue["Members@odata.count"] =
1798 memberArray.size();
1799 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001800}
1801
Ninad Palsule97e90da2023-05-17 14:04:52 -05001802inline void processAfterCreateUser(
1803 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1804 const std::string& username, const std::string& password,
1805 const boost::system::error_code& ec, sdbusplus::message_t& m)
1806{
1807 if (ec)
1808 {
1809 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1810 return;
1811 }
1812
1813 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1814 {
1815 // At this point we have a user that's been
1816 // created, but the password set
1817 // failed.Something is wrong, so delete the user
1818 // that we've already created
1819 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1820 tempObjPath /= username;
1821 const std::string userPath(tempObjPath);
1822
1823 crow::connections::systemBus->async_method_call(
1824 [asyncResp, password](const boost::system::error_code& ec3) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001825 if (ec3)
1826 {
1827 messages::internalError(asyncResp->res);
1828 return;
1829 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001830
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001831 // If password is invalid
1832 messages::propertyValueFormatError(asyncResp->res, nullptr,
1833 "Password");
1834 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001835 "xyz.openbmc_project.User.Manager", userPath,
1836 "xyz.openbmc_project.Object.Delete", "Delete");
1837
Ed Tanous62598e32023-07-17 17:06:25 -07001838 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
Ninad Palsule97e90da2023-05-17 14:04:52 -05001839 return;
1840 }
1841
1842 messages::created(asyncResp->res);
1843 asyncResp->res.addHeader("Location",
1844 "/redfish/v1/AccountService/Accounts/" + username);
1845}
1846
1847inline void processAfterGetAllGroups(
1848 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1849 const std::string& username, const std::string& password,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001850 const std::string& roleId, bool enabled,
Ninad Palsule9ba73932023-06-01 16:38:57 -05001851 std::optional<std::vector<std::string>> accountTypes,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001852 const std::vector<std::string>& allGroupsList)
Ninad Palsule97e90da2023-05-17 14:04:52 -05001853{
Ninad Palsule3e72c202023-03-27 17:19:55 -05001854 std::vector<std::string> userGroups;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001855 std::vector<std::string> accountTypeUserGroups;
1856
1857 // If user specified account types then convert them to unix user groups
1858 if (accountTypes)
1859 {
1860 if (!getUserGroupFromAccountType(asyncResp->res, *accountTypes,
1861 accountTypeUserGroups))
1862 {
1863 // Problem in mapping Account Types to User Groups, Error already
1864 // logged.
1865 return;
1866 }
1867 }
1868
Ninad Palsule3e72c202023-03-27 17:19:55 -05001869 for (const auto& grp : allGroupsList)
1870 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001871 // If user specified the account type then only accept groups which are
1872 // in the account types group list.
1873 if (!accountTypeUserGroups.empty())
1874 {
1875 bool found = false;
1876 for (const auto& grp1 : accountTypeUserGroups)
1877 {
1878 if (grp == grp1)
1879 {
1880 found = true;
1881 break;
1882 }
1883 }
1884 if (!found)
1885 {
1886 continue;
1887 }
1888 }
1889
Ninad Palsule3e72c202023-03-27 17:19:55 -05001890 // Console access is provided to the user who is a member of
1891 // hostconsole group and has a administrator role. So, set
1892 // hostconsole group only for the administrator.
Ninad Palsule9ba73932023-06-01 16:38:57 -05001893 if ((grp == "hostconsole") && (roleId != "priv-admin"))
Ninad Palsule3e72c202023-03-27 17:19:55 -05001894 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001895 if (!accountTypeUserGroups.empty())
1896 {
Ed Tanous62598e32023-07-17 17:06:25 -07001897 BMCWEB_LOG_ERROR(
1898 "Only administrator can get HostConsole access");
Ninad Palsule9ba73932023-06-01 16:38:57 -05001899 asyncResp->res.result(boost::beast::http::status::bad_request);
1900 return;
1901 }
1902 continue;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001903 }
Ninad Palsule9ba73932023-06-01 16:38:57 -05001904 userGroups.emplace_back(grp);
1905 }
1906
1907 // Make sure user specified groups are valid. This is internal error because
1908 // it some inconsistencies between user manager and bmcweb.
1909 if (!accountTypeUserGroups.empty() &&
1910 accountTypeUserGroups.size() != userGroups.size())
1911 {
1912 messages::internalError(asyncResp->res);
1913 return;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001914 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001915 crow::connections::systemBus->async_method_call(
1916 [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
2194 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002195 [asyncResp, username](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002196 if (ec)
2197 {
2198 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2199 username);
2200 return;
2201 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002202
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002203 messages::accountRemoved(asyncResp->res);
2204 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002205 "xyz.openbmc_project.User.Manager", userPath,
2206 "xyz.openbmc_project.Object.Delete", "Delete");
2207}
2208
Patrick Williams504af5a2025-02-03 14:29:03 -05002209inline void handleAccountPatch(
2210 App& app, const crow::Request& req,
2211 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2212 const std::string& username)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002213{
2214 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2215 {
2216 return;
2217 }
Ed Tanous25b54db2024-04-17 15:40:31 -07002218 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2219 {
2220 // If authentication is disabled, there are no user accounts
2221 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2222 return;
2223 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002224 std::optional<std::string> newUserName;
2225 std::optional<std::string> password;
2226 std::optional<bool> enabled;
2227 std::optional<std::string> roleId;
2228 std::optional<bool> locked;
Abhishek Patel58345852022-02-02 08:54:25 -06002229 std::optional<std::vector<std::string>> accountTypes;
2230
Ed Tanous1ef4c342022-05-12 16:12:36 -07002231 if (req.session == nullptr)
2232 {
2233 messages::internalError(asyncResp->res);
2234 return;
2235 }
2236
Ed Tanous2b9c1df2024-04-06 13:52:01 -07002237 bool userSelf = (username == req.session->username);
2238
Ed Tanous1ef4c342022-05-12 16:12:36 -07002239 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002240 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002241 Privileges configureUsers = {"ConfigureUsers"};
2242 bool userHasConfigureUsers =
2243 effectiveUserPrivileges.isSupersetOf(configureUsers);
2244 if (userHasConfigureUsers)
2245 {
2246 // Users with ConfigureUsers can modify for all users
Patrick Williams504af5a2025-02-03 14:29:03 -05002247 if (!json_util::readJsonPatch( //
2248 req, asyncResp->res, //
Myung Baeafc474a2024-10-09 00:53:29 -07002249 "AccountTypes", accountTypes, //
Patrick Williams504af5a2025-02-03 14:29:03 -05002250 "Enabled", enabled, //
2251 "Locked", locked, //
2252 "Password", password, //
2253 "RoleId", roleId, //
2254 "UserName", newUserName //
Myung Baeafc474a2024-10-09 00:53:29 -07002255 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07002256 {
2257 return;
2258 }
2259 }
2260 else
2261 {
2262 // ConfigureSelf accounts can only modify their own account
Abhishek Patel58345852022-02-02 08:54:25 -06002263 if (!userSelf)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002264 {
2265 messages::insufficientPrivilege(asyncResp->res);
2266 return;
2267 }
2268
2269 // ConfigureSelf accounts can only modify their password
2270 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
2271 password))
2272 {
2273 return;
2274 }
2275 }
2276
2277 // if user name is not provided in the patch method or if it
2278 // matches the user name in the URI, then we are treating it as
2279 // updating user properties other then username. If username
2280 // provided doesn't match the URI, then we are treating this as
2281 // user rename request.
2282 if (!newUserName || (newUserName.value() == username))
2283 {
2284 updateUserProperties(asyncResp, username, password, enabled, roleId,
Ravi Tejae518ef32024-05-16 10:33:08 -05002285 locked, accountTypes, userSelf, req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002286 return;
2287 }
2288 crow::connections::systemBus->async_method_call(
2289 [asyncResp, username, password(std::move(password)),
2290 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Ravi Tejae518ef32024-05-16 10:33:08 -05002291 locked, userSelf, req, accountTypes(std::move(accountTypes))](
Ed Tanouse81de512023-06-27 17:07:00 -07002292 const boost::system::error_code& ec, sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002293 if (ec)
2294 {
2295 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
2296 username);
2297 return;
2298 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002299
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002300 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
2301 locked, accountTypes, userSelf, req.session);
2302 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002303 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
2304 "xyz.openbmc_project.User.Manager", "RenameUser", username,
2305 *newUserName);
2306}
2307
Ed Tanous6c51eab2021-06-03 12:30:29 -07002308inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07002309{
Ed Tanous6c51eab2021-06-03 12:30:29 -07002310 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002311 .privileges(redfish::privileges::headAccountService)
2312 .methods(boost::beast::http::verb::head)(
2313 std::bind_front(handleAccountServiceHead, std::ref(app)));
2314
2315 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07002316 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002317 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002318 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002319
Ed Tanousf5ffd802021-07-19 10:55:33 -07002320 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06002321 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07002322 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002323 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07002324
Ed Tanous1aa375b2024-04-13 11:51:10 -07002325 BMCWEB_ROUTE(
2326 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002327 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002328 .privileges(redfish::privileges::headCertificateCollection)
2329 .methods(boost::beast::http::verb::head)(std::bind_front(
2330 handleAccountServiceClientCertificatesHead, std::ref(app)));
2331
2332 BMCWEB_ROUTE(
2333 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002334 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002335 .privileges(redfish::privileges::getCertificateCollection)
2336 .methods(boost::beast::http::verb::get)(std::bind_front(
2337 handleAccountServiceClientCertificatesGet, std::ref(app)));
2338
2339 BMCWEB_ROUTE(
2340 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002341 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002342 .privileges(redfish::privileges::headCertificate)
2343 .methods(boost::beast::http::verb::head)(std::bind_front(
2344 handleAccountServiceClientCertificatesInstanceHead, std::ref(app)));
2345
2346 BMCWEB_ROUTE(
2347 app,
2348 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
2349 .privileges(redfish::privileges::getCertificate)
2350 .methods(boost::beast::http::verb::get)(std::bind_front(
2351 handleAccountServiceClientCertificatesInstanceGet, std::ref(app)));
2352
Ed Tanous6c51eab2021-06-03 12:30:29 -07002353 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002354 .privileges(redfish::privileges::headManagerAccountCollection)
2355 .methods(boost::beast::http::verb::head)(
2356 std::bind_front(handleAccountCollectionHead, std::ref(app)));
2357
2358 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002359 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002360 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002361 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07002362
Ed Tanous6c51eab2021-06-03 12:30:29 -07002363 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002364 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002365 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002366 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07002367
2368 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002369 .privileges(redfish::privileges::headManagerAccount)
2370 .methods(boost::beast::http::verb::head)(
2371 std::bind_front(handleAccountHead, std::ref(app)));
2372
2373 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07002374 .privileges(redfish::privileges::getManagerAccount)
2375 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002376 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002377
2378 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002379 // TODO this privilege should be using the generated endpoints, but
2380 // because of the special handling of ConfigureSelf, it's not able to
2381 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07002382 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
2383 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002384 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002385
2386 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002387 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002388 .methods(boost::beast::http::verb::delete_)(
Gunnar Mills20fc3072023-01-27 15:13:36 -06002389 std::bind_front(handleAccountDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002390}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002391
Ed Tanous1abe55e2018-09-05 08:30:59 -07002392} // namespace redfish