blob: bc4706b6039af826ea45d431d4d358bda5f44d02 [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 Tanous3ccb3ad2023-01-13 17:40:03 -08006#include "app.hpp"
Gunnar Millsa0735a42024-09-06 12:51:11 -05007#include "boost_formatters.hpp"
Ed Tanous1aa375b2024-04-13 11:51:10 -07008#include "certificate_service.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08009#include "dbus_utility.hpp"
10#include "error_messages.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070011#include "generated/enums/account_service.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080012#include "persistent_data.hpp"
13#include "query.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070014#include "registries/privilege_registry.hpp"
Ed Tanous3281bcf2024-06-25 16:02:05 -070015#include "sessions.hpp"
Ed Tanous1aa375b2024-04-13 11:51:10 -070016#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080017#include "utils/dbus_utils.hpp"
18#include "utils/json_utils.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070019
Ed Tanous1aa375b2024-04-13 11:51:10 -070020#include <boost/url/format.hpp>
21#include <boost/url/url.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070022#include <sdbusplus/asio/property.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020023#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050024
George Liu2b731192023-01-11 16:27:13 +080025#include <array>
Ed Tanous1aa375b2024-04-13 11:51:10 -070026#include <memory>
Abhishek Patelc7229812022-02-01 10:07:15 -060027#include <optional>
Ed Tanous3544d2a2023-08-06 18:12:20 -070028#include <ranges>
Abhishek Patelc7229812022-02-01 10:07:15 -060029#include <string>
George Liu2b731192023-01-11 16:27:13 +080030#include <string_view>
Ed Tanous20fa6a22024-05-20 18:02:58 -070031#include <utility>
Abhishek Patelc7229812022-02-01 10:07:15 -060032#include <vector>
33
Ed Tanous1abe55e2018-09-05 08:30:59 -070034namespace redfish
35{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010036
Ed Tanous23a21a12020-07-25 04:45:05 +000037constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053038 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070039constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053040 "/xyz/openbmc_project/user/ldap/active_directory";
41
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053042constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053043constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
44constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
45constexpr const char* ldapConfigInterface =
46 "xyz.openbmc_project.User.Ldap.Config";
47constexpr const char* ldapCreateInterface =
48 "xyz.openbmc_project.User.Ldap.Create";
49constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053050constexpr const char* ldapPrivMapperInterface =
51 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053052
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060053struct LDAPRoleMapData
54{
55 std::string groupName;
56 std::string privilege;
57};
58
Ratan Gupta6973a582018-12-13 18:25:44 +053059struct LDAPConfigData
60{
Ed Tanous47f29342024-03-19 12:18:06 -070061 std::string uri;
62 std::string bindDN;
63 std::string baseDN;
64 std::string searchScope;
65 std::string serverType;
Ratan Gupta6973a582018-12-13 18:25:44 +053066 bool serviceEnabled = false;
Ed Tanous47f29342024-03-19 12:18:06 -070067 std::string userNameAttribute;
68 std::string groupAttribute;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060069 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053070};
71
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060072inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053073{
74 if (role == "priv-admin")
75 {
76 return "Administrator";
77 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070078 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053079 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053080 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053081 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070082 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053083 {
84 return "Operator";
85 }
86 return "";
87}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060088inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053089{
90 if (role == "Administrator")
91 {
92 return "priv-admin";
93 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070094 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053095 {
96 return "priv-user";
97 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070098 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053099 {
100 return "priv-operator";
101 }
102 return "";
103}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700104
Abhishek Patelc7229812022-02-01 10:07:15 -0600105/**
106 * @brief Maps user group names retrieved from D-Bus object to
107 * Account Types.
108 *
109 * @param[in] userGroups List of User groups
110 * @param[out] res AccountTypes populated
111 *
112 * @return true in case of success, false if UserGroups contains
113 * invalid group name(s).
114 */
115inline bool translateUserGroup(const std::vector<std::string>& userGroups,
116 crow::Response& res)
117{
118 std::vector<std::string> accountTypes;
119 for (const auto& userGroup : userGroups)
120 {
121 if (userGroup == "redfish")
122 {
123 accountTypes.emplace_back("Redfish");
124 accountTypes.emplace_back("WebUI");
125 }
126 else if (userGroup == "ipmi")
127 {
128 accountTypes.emplace_back("IPMI");
129 }
130 else if (userGroup == "ssh")
131 {
Abhishek Patelc7229812022-02-01 10:07:15 -0600132 accountTypes.emplace_back("ManagerConsole");
133 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500134 else if (userGroup == "hostconsole")
135 {
136 // The hostconsole group controls who can access the host console
137 // port via ssh and websocket.
138 accountTypes.emplace_back("HostConsole");
139 }
Abhishek Patelc7229812022-02-01 10:07:15 -0600140 else if (userGroup == "web")
141 {
142 // 'web' is one of the valid groups in the UserGroups property of
143 // the user account in the D-Bus object. This group is currently not
144 // doing anything, and is considered to be equivalent to 'redfish'.
145 // 'redfish' user group is mapped to 'Redfish'and 'WebUI'
146 // AccountTypes, so do nothing here...
147 }
148 else
149 {
Ed Tanous8ece0e42024-01-02 13:16:50 -0800150 // Invalid user group name. Caller throws an exception.
Abhishek Patelc7229812022-02-01 10:07:15 -0600151 return false;
152 }
153 }
154
155 res.jsonValue["AccountTypes"] = std::move(accountTypes);
156 return true;
157}
158
Abhishek Patel58345852022-02-02 08:54:25 -0600159/**
160 * @brief Builds User Groups from the Account Types
161 *
162 * @param[in] asyncResp Async Response
163 * @param[in] accountTypes List of Account Types
164 * @param[out] userGroups List of User Groups mapped from Account Types
165 *
166 * @return true if Account Types mapped to User Groups, false otherwise.
167 */
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400168inline bool getUserGroupFromAccountType(
169 crow::Response& res, const std::vector<std::string>& accountTypes,
170 std::vector<std::string>& userGroups)
Abhishek Patel58345852022-02-02 08:54:25 -0600171{
172 // Need both Redfish and WebUI Account Types to map to 'redfish' User Group
173 bool redfishType = false;
174 bool webUIType = false;
175
176 for (const auto& accountType : accountTypes)
177 {
178 if (accountType == "Redfish")
179 {
180 redfishType = true;
181 }
182 else if (accountType == "WebUI")
183 {
184 webUIType = true;
185 }
186 else if (accountType == "IPMI")
187 {
188 userGroups.emplace_back("ipmi");
189 }
190 else if (accountType == "HostConsole")
191 {
192 userGroups.emplace_back("hostconsole");
193 }
194 else if (accountType == "ManagerConsole")
195 {
196 userGroups.emplace_back("ssh");
197 }
198 else
199 {
200 // Invalid Account Type
201 messages::propertyValueNotInList(res, "AccountTypes", accountType);
202 return false;
203 }
204 }
205
206 // Both Redfish and WebUI Account Types are needed to PATCH
207 if (redfishType ^ webUIType)
208 {
Ed Tanous62598e32023-07-17 17:06:25 -0700209 BMCWEB_LOG_ERROR(
210 "Missing Redfish or WebUI Account Type to set redfish User Group");
Abhishek Patel58345852022-02-02 08:54:25 -0600211 messages::strictAccountTypes(res, "AccountTypes");
212 return false;
213 }
214
215 if (redfishType && webUIType)
216 {
217 userGroups.emplace_back("redfish");
218 }
219
220 return true;
221}
222
223/**
224 * @brief Sets UserGroups property of the user based on the Account Types
225 *
226 * @param[in] accountTypes List of User Account Types
227 * @param[in] asyncResp Async Response
228 * @param[in] dbusObjectPath D-Bus Object Path
229 * @param[in] userSelf true if User is updating OWN Account Types
230 */
231inline void
232 patchAccountTypes(const std::vector<std::string>& accountTypes,
233 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
234 const std::string& dbusObjectPath, bool userSelf)
235{
236 // Check if User is disabling own Redfish Account Type
237 if (userSelf &&
238 (accountTypes.cend() ==
239 std::find(accountTypes.cbegin(), accountTypes.cend(), "Redfish")))
240 {
Ed Tanous62598e32023-07-17 17:06:25 -0700241 BMCWEB_LOG_ERROR(
242 "User disabling OWN Redfish Account Type is not allowed");
Abhishek Patel58345852022-02-02 08:54:25 -0600243 messages::strictAccountTypes(asyncResp->res, "AccountTypes");
244 return;
245 }
246
247 std::vector<std::string> updatedUserGroups;
248 if (!getUserGroupFromAccountType(asyncResp->res, accountTypes,
249 updatedUserGroups))
250 {
251 // Problem in mapping Account Types to User Groups, Error already
252 // logged.
253 return;
254 }
Ginu Georgee93abac2024-06-14 17:35:27 +0530255 setDbusProperty(asyncResp, "AccountTypes",
256 "xyz.openbmc_project.User.Manager", dbusObjectPath,
257 "xyz.openbmc_project.User.Attributes", "UserGroups",
258 updatedUserGroups);
Abhishek Patel58345852022-02-02 08:54:25 -0600259}
260
zhanghch058d1b46d2021-04-01 11:18:24 +0800261inline void userErrorMessageHandler(
262 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
263 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000264{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000265 if (e == nullptr)
266 {
267 messages::internalError(asyncResp->res);
268 return;
269 }
270
Manojkiran Eda055806b2020-11-03 09:36:28 +0530271 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000272 if (strcmp(errorMessage,
273 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
274 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800275 messages::resourceAlreadyExists(asyncResp->res, "ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000276 "UserName", newUser);
277 }
278 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
279 "UserNameDoesNotExist") == 0)
280 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800281 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000282 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700283 else if ((strcmp(errorMessage,
284 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
285 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800286 (strcmp(
287 errorMessage,
288 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
289 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000290 {
291 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
292 }
293 else if (strcmp(errorMessage,
294 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
295 {
296 messages::createLimitReachedForResource(asyncResp->res);
297 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000298 else
299 {
Gunnar Millsb8ad5832023-10-02 16:26:07 -0500300 BMCWEB_LOG_ERROR("DBUS response error {}", errorMessage);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000301 messages::internalError(asyncResp->res);
302 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000303}
304
Ed Tanous81ce6092020-12-17 16:54:55 +0000305inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000306 const LDAPConfigData& confData,
307 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530308{
Ed Tanous49cc2632024-03-20 12:49:15 -0700309 nlohmann::json::object_t ldap;
Ed Tanous14766872022-03-15 10:44:42 -0700310 ldap["ServiceEnabled"] = confData.serviceEnabled;
Ed Tanous49cc2632024-03-20 12:49:15 -0700311 nlohmann::json::array_t serviceAddresses;
312 serviceAddresses.emplace_back(confData.uri);
313 ldap["ServiceAddresses"] = std::move(serviceAddresses);
314
315 nlohmann::json::object_t authentication;
316 authentication["AuthenticationType"] =
Ed Tanous0ec8b832022-03-14 14:56:47 -0700317 account_service::AuthenticationTypes::UsernameAndPassword;
Ed Tanous49cc2632024-03-20 12:49:15 -0700318 authentication["Username"] = confData.bindDN;
319 authentication["Password"] = nullptr;
320 ldap["Authentication"] = std::move(authentication);
Ed Tanous14766872022-03-15 10:44:42 -0700321
Ed Tanous49cc2632024-03-20 12:49:15 -0700322 nlohmann::json::object_t ldapService;
323 nlohmann::json::object_t searchSettings;
324 nlohmann::json::array_t baseDistinguishedNames;
325 baseDistinguishedNames.emplace_back(confData.baseDN);
Ed Tanous14766872022-03-15 10:44:42 -0700326
Ed Tanous49cc2632024-03-20 12:49:15 -0700327 searchSettings["BaseDistinguishedNames"] =
328 std::move(baseDistinguishedNames);
329 searchSettings["UsernameAttribute"] = confData.userNameAttribute;
330 searchSettings["GroupsAttribute"] = confData.groupAttribute;
331 ldapService["SearchSettings"] = std::move(searchSettings);
332 ldap["LDAPService"] = std::move(ldapService);
333
334 nlohmann::json::array_t roleMapArray;
Ed Tanous9eb808c2022-01-25 10:19:23 -0800335 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600336 {
Ed Tanous62598e32023-07-17 17:06:25 -0700337 BMCWEB_LOG_DEBUG("Pushing the data groupName={}", obj.second.groupName);
Ed Tanous613dabe2022-07-09 11:17:36 -0700338
Ed Tanous613dabe2022-07-09 11:17:36 -0700339 nlohmann::json::object_t remoteGroup;
340 remoteGroup["RemoteGroup"] = obj.second.groupName;
Jorge Cisneros329f0342022-11-04 16:26:25 +0000341 remoteGroup["LocalRole"] = getRoleIdFromPrivilege(obj.second.privilege);
342 roleMapArray.emplace_back(std::move(remoteGroup));
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600343 }
Ed Tanous49cc2632024-03-20 12:49:15 -0700344
345 ldap["RemoteRoleMapping"] = std::move(roleMapArray);
346
347 jsonResponse[ldapType].update(ldap);
Ratan Gupta6973a582018-12-13 18:25:44 +0530348}
349
350/**
Ratan Gupta06785242019-07-26 22:30:16 +0530351 * @brief validates given JSON input and then calls appropriate method to
352 * create, to delete or to set Rolemapping object based on the given input.
353 *
354 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000355inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800356 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530357 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousc1019822024-03-06 12:54:38 -0800358 const std::string& serverType,
359 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530360{
361 for (size_t index = 0; index < input.size(); index++)
362 {
Ed Tanousc1019822024-03-06 12:54:38 -0800363 std::variant<nlohmann::json::object_t, std::nullptr_t>& thisJson =
364 input[index];
365 nlohmann::json::object_t* obj =
366 std::get_if<nlohmann::json::object_t>(&thisJson);
367 if (obj == nullptr)
Ratan Gupta06785242019-07-26 22:30:16 +0530368 {
369 // delete the existing object
370 if (index < roleMapObjData.size())
371 {
372 crow::connections::systemBus->async_method_call(
373 [asyncResp, roleMapObjData, serverType,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800374 index](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400375 if (ec)
376 {
377 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
378 messages::internalError(asyncResp->res);
379 return;
380 }
381 asyncResp->res
382 .jsonValue[serverType]["RemoteRoleMapping"][index] =
383 nullptr;
384 },
Ratan Gupta06785242019-07-26 22:30:16 +0530385 ldapDbusService, roleMapObjData[index].first,
386 "xyz.openbmc_project.Object.Delete", "Delete");
387 }
388 else
389 {
Ed Tanous62598e32023-07-17 17:06:25 -0700390 BMCWEB_LOG_ERROR("Can't delete the object");
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400391 messages::propertyValueTypeError(
392 asyncResp->res, "null",
393 "RemoteRoleMapping/" + std::to_string(index));
Ratan Gupta06785242019-07-26 22:30:16 +0530394 return;
395 }
396 }
Ed Tanousc1019822024-03-06 12:54:38 -0800397 else if (obj->empty())
Ratan Gupta06785242019-07-26 22:30:16 +0530398 {
399 // Don't do anything for the empty objects,parse next json
400 // eg {"RemoteRoleMapping",[{}]}
401 }
402 else
403 {
404 // update/create the object
405 std::optional<std::string> remoteGroup;
406 std::optional<std::string> localRole;
407
Myung Baeafc474a2024-10-09 00:53:29 -0700408 if (!json_util::readJsonObject( //
409 *obj, asyncResp->res, //
410 "LocalRole", localRole, //
411 "RemoteGroup", remoteGroup //
412 ))
Ratan Gupta06785242019-07-26 22:30:16 +0530413 {
414 continue;
415 }
416
417 // Update existing RoleMapping Object
418 if (index < roleMapObjData.size())
419 {
Ed Tanous62598e32023-07-17 17:06:25 -0700420 BMCWEB_LOG_DEBUG("Update Role Map Object");
Ratan Gupta06785242019-07-26 22:30:16 +0530421 // If "RemoteGroup" info is provided
422 if (remoteGroup)
423 {
Ed Tanousd02aad32024-02-13 14:43:34 -0800424 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530425 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800426 std::format("RemoteRoleMapping/{}/RemoteGroup", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530427 ldapDbusService, roleMapObjData[index].first,
428 "xyz.openbmc_project.User.PrivilegeMapperEntry",
429 "GroupName", *remoteGroup);
Ratan Gupta06785242019-07-26 22:30:16 +0530430 }
431
432 // If "LocalRole" info is provided
433 if (localRole)
434 {
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500435 std::string priv = getPrivilegeFromRoleId(*localRole);
436 if (priv.empty())
437 {
438 messages::propertyValueNotInList(
439 asyncResp->res, *localRole,
440 std::format("RemoteRoleMapping/{}/LocalRole",
441 index));
442 return;
443 }
Ed Tanousd02aad32024-02-13 14:43:34 -0800444 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530445 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800446 std::format("RemoteRoleMapping/{}/LocalRole", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530447 ldapDbusService, roleMapObjData[index].first,
448 "xyz.openbmc_project.User.PrivilegeMapperEntry",
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500449 "Privilege", priv);
Ratan Gupta06785242019-07-26 22:30:16 +0530450 }
451 }
452 // Create a new RoleMapping Object.
453 else
454 {
Ed Tanous62598e32023-07-17 17:06:25 -0700455 BMCWEB_LOG_DEBUG(
456 "setRoleMappingProperties: Creating new Object");
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400457 std::string pathString =
458 "RemoteRoleMapping/" + std::to_string(index);
Ratan Gupta06785242019-07-26 22:30:16 +0530459
460 if (!localRole)
461 {
462 messages::propertyMissing(asyncResp->res,
463 pathString + "/LocalRole");
464 continue;
465 }
466 if (!remoteGroup)
467 {
468 messages::propertyMissing(asyncResp->res,
469 pathString + "/RemoteGroup");
470 continue;
471 }
472
473 std::string dbusObjectPath;
474 if (serverType == "ActiveDirectory")
475 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700476 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530477 }
478 else if (serverType == "LDAP")
479 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000480 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530481 }
482
Ed Tanous62598e32023-07-17 17:06:25 -0700483 BMCWEB_LOG_DEBUG("Remote Group={},LocalRole={}", *remoteGroup,
484 *localRole);
Ratan Gupta06785242019-07-26 22:30:16 +0530485
486 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700487 [asyncResp, serverType, localRole,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800488 remoteGroup](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400489 if (ec)
490 {
491 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
492 messages::internalError(asyncResp->res);
493 return;
494 }
495 nlohmann::json& remoteRoleJson =
496 asyncResp->res
497 .jsonValue[serverType]["RemoteRoleMapping"];
498 nlohmann::json::object_t roleMapEntry;
499 roleMapEntry["LocalRole"] = *localRole;
500 roleMapEntry["RemoteGroup"] = *remoteGroup;
501 remoteRoleJson.emplace_back(std::move(roleMapEntry));
502 },
Ratan Gupta06785242019-07-26 22:30:16 +0530503 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700504 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530505 getPrivilegeFromRoleId(std::move(*localRole)));
506 }
507 }
508 }
509}
510
511/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530512 * Function that retrieves all properties for LDAP config object
513 * into JSON
514 */
515template <typename CallbackFunc>
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400516inline void
517 getLDAPConfigData(const std::string& ldapType, CallbackFunc&& callback)
Ratan Gupta6973a582018-12-13 18:25:44 +0530518{
George Liu2b731192023-01-11 16:27:13 +0800519 constexpr std::array<std::string_view, 2> interfaces = {
520 ldapEnableInterface, ldapConfigInterface};
Ratan Gupta6973a582018-12-13 18:25:44 +0530521
George Liu2b731192023-01-11 16:27:13 +0800522 dbus::utility::getDbusObject(
523 ldapConfigObjectName, interfaces,
Ed Tanous8cb2c022024-03-27 16:31:46 -0700524 [callback = std::forward<CallbackFunc>(callback),
Ed Tanousc1019822024-03-06 12:54:38 -0800525 ldapType](const boost::system::error_code& ec,
526 const dbus::utility::MapperGetObject& resp) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400527 if (ec || resp.empty())
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600528 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400529 BMCWEB_LOG_WARNING(
530 "DBUS response error during getting of service name: {}",
531 ec);
532 LDAPConfigData empty{};
533 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600534 return;
535 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400536 std::string service = resp.begin()->first;
537 sdbusplus::message::object_path path(ldapRootObject);
538 dbus::utility::getManagedObjects(
539 service, path,
540 [callback, ldapType](const boost::system::error_code& ec2,
541 const dbus::utility::ManagedObjectType&
542 ldapObjects) mutable {
543 LDAPConfigData confData{};
544 if (ec2)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600545 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400546 callback(false, confData, ldapType);
547 BMCWEB_LOG_WARNING("D-Bus responses error: {}", ec2);
548 return;
549 }
550
551 std::string ldapDbusType;
552 std::string searchString;
553
554 if (ldapType == "LDAP")
555 {
556 ldapDbusType =
557 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
558 searchString = "openldap";
559 }
560 else if (ldapType == "ActiveDirectory")
561 {
562 ldapDbusType =
563 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
564 searchString = "active_directory";
565 }
566 else
567 {
568 BMCWEB_LOG_ERROR(
569 "Can't get the DbusType for the given type={}",
570 ldapType);
571 callback(false, confData, ldapType);
572 return;
573 }
574
575 std::string ldapEnableInterfaceStr = ldapEnableInterface;
576 std::string ldapConfigInterfaceStr = ldapConfigInterface;
577
578 for (const auto& object : ldapObjects)
579 {
580 // let's find the object whose ldap type is equal to the
581 // given type
582 if (object.first.str.find(searchString) ==
583 std::string::npos)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600584 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400585 continue;
586 }
587
588 for (const auto& interface : object.second)
589 {
590 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600591 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400592 // rest of the properties are string.
593 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600594 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400595 if (property.first == "Enabled")
596 {
597 const bool* value =
598 std::get_if<bool>(&property.second);
599 if (value == nullptr)
600 {
601 continue;
602 }
603 confData.serviceEnabled = *value;
604 break;
605 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600606 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400607 }
608 else if (interface.first == ldapConfigInterfaceStr)
609 {
610 for (const auto& property : interface.second)
611 {
612 const std::string* strValue =
613 std::get_if<std::string>(
614 &property.second);
615 if (strValue == nullptr)
616 {
617 continue;
618 }
619 if (property.first == "LDAPServerURI")
620 {
621 confData.uri = *strValue;
622 }
623 else if (property.first == "LDAPBindDN")
624 {
625 confData.bindDN = *strValue;
626 }
627 else if (property.first == "LDAPBaseDN")
628 {
629 confData.baseDN = *strValue;
630 }
631 else if (property.first ==
632 "LDAPSearchScope")
633 {
634 confData.searchScope = *strValue;
635 }
636 else if (property.first ==
637 "GroupNameAttribute")
638 {
639 confData.groupAttribute = *strValue;
640 }
641 else if (property.first ==
642 "UserNameAttribute")
643 {
644 confData.userNameAttribute = *strValue;
645 }
646 else if (property.first == "LDAPType")
647 {
648 confData.serverType = *strValue;
649 }
650 }
651 }
652 else if (
653 interface.first ==
654 "xyz.openbmc_project.User.PrivilegeMapperEntry")
655 {
656 LDAPRoleMapData roleMapData{};
657 for (const auto& property : interface.second)
658 {
659 const std::string* strValue =
660 std::get_if<std::string>(
661 &property.second);
662
663 if (strValue == nullptr)
664 {
665 continue;
666 }
667
668 if (property.first == "GroupName")
669 {
670 roleMapData.groupName = *strValue;
671 }
672 else if (property.first == "Privilege")
673 {
674 roleMapData.privilege = *strValue;
675 }
676 }
677
678 confData.groupRoleList.emplace_back(
679 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600680 }
681 }
682 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400683 callback(true, confData, ldapType);
684 });
George Liu2b731192023-01-11 16:27:13 +0800685 });
Ratan Gupta6973a582018-12-13 18:25:44 +0530686}
687
Ed Tanous6c51eab2021-06-03 12:30:29 -0700688/**
Ed Tanous6c51eab2021-06-03 12:30:29 -0700689 * @brief updates the LDAP server address and updates the
690 json response with the new value.
691 * @param serviceAddressList address to be updated.
692 * @param asyncResp pointer to the JSON response
693 * @param ldapServerElementName Type of LDAP
694 server(openLDAP/ActiveDirectory)
695 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530696
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700697inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700698 const std::vector<std::string>& serviceAddressList,
699 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
700 const std::string& ldapServerElementName,
701 const std::string& ldapConfigObject)
702{
Ginu Georgee93abac2024-06-14 17:35:27 +0530703 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceAddress",
704 ldapDbusService, ldapConfigObject, ldapConfigInterface,
705 "LDAPServerURI", serviceAddressList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700706}
707/**
708 * @brief updates the LDAP Bind DN and updates the
709 json response with the new value.
710 * @param username name of the user which needs to be updated.
711 * @param asyncResp pointer to the JSON response
712 * @param ldapServerElementName Type of LDAP
713 server(openLDAP/ActiveDirectory)
714 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530715
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700716inline void
717 handleUserNamePatch(const std::string& username,
718 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
719 const std::string& ldapServerElementName,
720 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700721{
Ginu Georgee93abac2024-06-14 17:35:27 +0530722 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800723 ldapServerElementName + "/Authentication/Username",
Ginu Georgee93abac2024-06-14 17:35:27 +0530724 ldapDbusService, ldapConfigObject, ldapConfigInterface,
725 "LDAPBindDN", username);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700726}
727
728/**
729 * @brief updates the LDAP password
730 * @param password : ldap password which needs to be updated.
731 * @param asyncResp pointer to the JSON response
732 * @param ldapServerElementName Type of LDAP
733 * server(openLDAP/ActiveDirectory)
734 */
735
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700736inline void
737 handlePasswordPatch(const std::string& password,
738 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
739 const std::string& ldapServerElementName,
740 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700741{
Ginu Georgee93abac2024-06-14 17:35:27 +0530742 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800743 ldapServerElementName + "/Authentication/Password",
Ginu Georgee93abac2024-06-14 17:35:27 +0530744 ldapDbusService, ldapConfigObject, ldapConfigInterface,
745 "LDAPBindDNPassword", password);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700746}
747
748/**
749 * @brief updates the LDAP BaseDN and updates the
750 json response with the new value.
751 * @param baseDNList baseDN list which needs to be updated.
752 * @param asyncResp pointer to the JSON response
753 * @param ldapServerElementName Type of LDAP
754 server(openLDAP/ActiveDirectory)
755 */
756
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700757inline void
758 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
759 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
760 const std::string& ldapServerElementName,
761 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700762{
Ginu Georgee93abac2024-06-14 17:35:27 +0530763 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800764 ldapServerElementName +
765 "/LDAPService/SearchSettings/BaseDistinguishedNames",
Ginu Georgee93abac2024-06-14 17:35:27 +0530766 ldapDbusService, ldapConfigObject, ldapConfigInterface,
767 "LDAPBaseDN", baseDNList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700768}
769/**
770 * @brief updates the LDAP user name attribute and updates the
771 json response with the new value.
772 * @param userNameAttribute attribute to be updated.
773 * @param asyncResp pointer to the JSON response
774 * @param ldapServerElementName Type of LDAP
775 server(openLDAP/ActiveDirectory)
776 */
777
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400778inline void handleUserNameAttrPatch(
779 const std::string& userNameAttribute,
780 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
781 const std::string& ldapServerElementName,
782 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700783{
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400784 setDbusProperty(
785 asyncResp,
786 ldapServerElementName + "LDAPService/SearchSettings/UsernameAttribute",
787 ldapDbusService, ldapConfigObject, ldapConfigInterface,
788 "UserNameAttribute", userNameAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700789}
790/**
791 * @brief updates the LDAP group attribute and updates the
792 json response with the new value.
793 * @param groupsAttribute attribute to be updated.
794 * @param asyncResp pointer to the JSON response
795 * @param ldapServerElementName Type of LDAP
796 server(openLDAP/ActiveDirectory)
797 */
798
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700799inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700800 const std::string& groupsAttribute,
801 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
802 const std::string& ldapServerElementName,
803 const std::string& ldapConfigObject)
804{
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400805 setDbusProperty(
806 asyncResp,
807 ldapServerElementName + "/LDAPService/SearchSettings/GroupsAttribute",
808 ldapDbusService, ldapConfigObject, ldapConfigInterface,
809 "GroupNameAttribute", groupsAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700810}
811/**
812 * @brief updates the LDAP service enable and updates the
813 json response with the new value.
814 * @param input JSON data.
815 * @param asyncResp pointer to the JSON response
816 * @param ldapServerElementName Type of LDAP
817 server(openLDAP/ActiveDirectory)
818 */
819
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700820inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700821 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
822 const std::string& ldapServerElementName,
823 const std::string& ldapConfigObject)
824{
Ginu Georgee93abac2024-06-14 17:35:27 +0530825 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceEnabled",
826 ldapDbusService, ldapConfigObject, ldapEnableInterface,
827 "Enabled", serviceEnabled);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700828}
829
Ed Tanousc1019822024-03-06 12:54:38 -0800830struct AuthMethods
Ed Tanous6c51eab2021-06-03 12:30:29 -0700831{
832 std::optional<bool> basicAuth;
833 std::optional<bool> cookie;
834 std::optional<bool> sessionToken;
835 std::optional<bool> xToken;
836 std::optional<bool> tls;
Ed Tanousc1019822024-03-06 12:54:38 -0800837};
Ed Tanous6c51eab2021-06-03 12:30:29 -0700838
Ed Tanousc1019822024-03-06 12:54:38 -0800839inline void
840 handleAuthMethodsPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
841 const AuthMethods& auth)
842{
843 persistent_data::AuthConfigMethods& authMethodsConfig =
Ed Tanous6c51eab2021-06-03 12:30:29 -0700844 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
845
Ed Tanousc1019822024-03-06 12:54:38 -0800846 if (auth.basicAuth)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700847 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700848 if constexpr (!BMCWEB_BASIC_AUTH)
849 {
850 messages::actionNotSupported(
851 asyncResp->res,
852 "Setting BasicAuth when basic-auth feature is disabled");
853 return;
854 }
855
Ed Tanousc1019822024-03-06 12:54:38 -0800856 authMethodsConfig.basic = *auth.basicAuth;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700857 }
858
Ed Tanousc1019822024-03-06 12:54:38 -0800859 if (auth.cookie)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700860 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700861 if constexpr (!BMCWEB_COOKIE_AUTH)
862 {
863 messages::actionNotSupported(
864 asyncResp->res,
865 "Setting Cookie when cookie-auth feature is disabled");
866 return;
867 }
Ed Tanousc1019822024-03-06 12:54:38 -0800868 authMethodsConfig.cookie = *auth.cookie;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700869 }
870
Ed Tanousc1019822024-03-06 12:54:38 -0800871 if (auth.sessionToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700872 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700873 if constexpr (!BMCWEB_SESSION_AUTH)
874 {
875 messages::actionNotSupported(
876 asyncResp->res,
877 "Setting SessionToken when session-auth feature is disabled");
878 return;
879 }
Ed Tanousc1019822024-03-06 12:54:38 -0800880 authMethodsConfig.sessionToken = *auth.sessionToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700881 }
882
Ed Tanousc1019822024-03-06 12:54:38 -0800883 if (auth.xToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700884 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700885 if constexpr (!BMCWEB_XTOKEN_AUTH)
886 {
887 messages::actionNotSupported(
888 asyncResp->res,
889 "Setting XToken when xtoken-auth feature is disabled");
890 return;
891 }
Ed Tanousc1019822024-03-06 12:54:38 -0800892 authMethodsConfig.xtoken = *auth.xToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700893 }
894
Ed Tanousc1019822024-03-06 12:54:38 -0800895 if (auth.tls)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700896 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700897 if constexpr (!BMCWEB_MUTUAL_TLS_AUTH)
898 {
899 messages::actionNotSupported(
900 asyncResp->res,
901 "Setting TLS when mutual-tls-auth feature is disabled");
902 return;
903 }
Ed Tanousc1019822024-03-06 12:54:38 -0800904 authMethodsConfig.tls = *auth.tls;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700905 }
906
907 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
908 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
909 !authMethodsConfig.tls)
910 {
911 // Do not allow user to disable everything
912 messages::actionNotSupported(asyncResp->res,
913 "of disabling all available methods");
914 return;
915 }
916
917 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
918 authMethodsConfig);
919 // Save configuration immediately
920 persistent_data::getConfig().writeData();
921
922 messages::success(asyncResp->res);
923}
924
925/**
926 * @brief Get the required values from the given JSON, validates the
927 * value and create the LDAP config object.
928 * @param input JSON data
929 * @param asyncResp pointer to the JSON response
930 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
931 */
932
Ed Tanous10cb44f2024-04-11 13:05:20 -0700933struct LdapPatchParams
934{
935 std::optional<std::string> authType;
936 std::optional<std::vector<std::string>> serviceAddressList;
937 std::optional<bool> serviceEnabled;
938 std::optional<std::vector<std::string>> baseDNList;
939 std::optional<std::string> userNameAttribute;
940 std::optional<std::string> groupsAttribute;
941 std::optional<std::string> userName;
942 std::optional<std::string> password;
943 std::optional<
944 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>
945 remoteRoleMapData;
946};
947
948inline void handleLDAPPatch(LdapPatchParams&& input,
Ed Tanous6c51eab2021-06-03 12:30:29 -0700949 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
950 const std::string& serverType)
951{
952 std::string dbusObjectPath;
953 if (serverType == "ActiveDirectory")
954 {
955 dbusObjectPath = adConfigObject;
956 }
957 else if (serverType == "LDAP")
958 {
959 dbusObjectPath = ldapConfigObjectName;
960 }
961 else
962 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700963 BMCWEB_LOG_ERROR("serverType wasn't AD or LDAP but was {}????",
964 serverType);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700965 return;
966 }
967
Ed Tanous10cb44f2024-04-11 13:05:20 -0700968 if (input.authType && *input.authType != "UsernameAndPassword")
Ed Tanous6c51eab2021-06-03 12:30:29 -0700969 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700970 messages::propertyValueNotInList(asyncResp->res, *input.authType,
Ed Tanousc1019822024-03-06 12:54:38 -0800971 "AuthenticationType");
972 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700973 }
Ed Tanousc1019822024-03-06 12:54:38 -0800974
Ed Tanous10cb44f2024-04-11 13:05:20 -0700975 if (input.serviceAddressList)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700976 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700977 if (input.serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530978 {
Ed Tanouse2616cc2022-06-27 12:45:55 -0700979 messages::propertyValueNotInList(
Ed Tanous10cb44f2024-04-11 13:05:20 -0700980 asyncResp->res, *input.serviceAddressList, "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +0000981 return;
982 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700983 }
Ed Tanous10cb44f2024-04-11 13:05:20 -0700984 if (input.baseDNList)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700985 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700986 if (input.baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +0530987 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700988 messages::propertyValueNotInList(asyncResp->res, *input.baseDNList,
Ed Tanous6c51eab2021-06-03 12:30:29 -0700989 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +0530990 return;
991 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700992 }
Ratan Gupta8a07d282019-03-16 08:33:47 +0530993
Ed Tanous6c51eab2021-06-03 12:30:29 -0700994 // nothing to update, then return
Ed Tanous10cb44f2024-04-11 13:05:20 -0700995 if (!input.userName && !input.password && !input.serviceAddressList &&
996 !input.baseDNList && !input.userNameAttribute &&
997 !input.groupsAttribute && !input.serviceEnabled &&
998 !input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700999 {
1000 return;
1001 }
1002
1003 // Get the existing resource first then keep modifying
1004 // whenever any property gets updated.
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001005 getLDAPConfigData(serverType, [asyncResp, input = std::move(input),
1006 dbusObjectPath = std::move(dbusObjectPath)](
1007 bool success,
1008 const LDAPConfigData& confData,
1009 const std::string& serverT) mutable {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001010 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301011 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001012 messages::internalError(asyncResp->res);
1013 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301014 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001015 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1016 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301017 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001018 // Disable the service first and update the rest of
1019 // the properties.
1020 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301021 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001022
Ed Tanous10cb44f2024-04-11 13:05:20 -07001023 if (input.serviceAddressList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301024 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001025 handleServiceAddressPatch(*input.serviceAddressList, asyncResp,
1026 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301027 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001028 if (input.userName)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001029 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001030 handleUserNamePatch(*input.userName, asyncResp, serverT,
1031 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001032 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001033 if (input.password)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001034 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001035 handlePasswordPatch(*input.password, asyncResp, serverT,
1036 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001037 }
1038
Ed Tanous10cb44f2024-04-11 13:05:20 -07001039 if (input.baseDNList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301040 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001041 handleBaseDNPatch(*input.baseDNList, asyncResp, serverT,
1042 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001043 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001044 if (input.userNameAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001045 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001046 handleUserNameAttrPatch(*input.userNameAttribute, asyncResp,
1047 serverT, dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001048 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001049 if (input.groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001050 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001051 handleGroupNameAttrPatch(*input.groupsAttribute, asyncResp, serverT,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001052 dbusObjectPath);
1053 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001054 if (input.serviceEnabled)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001055 {
1056 // if user has given the value as true then enable
1057 // the service. if user has given false then no-op
1058 // as service is already stopped.
Ed Tanous10cb44f2024-04-11 13:05:20 -07001059 if (*input.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301060 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001061 handleServiceEnablePatch(*input.serviceEnabled, asyncResp,
1062 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301063 }
1064 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001065 else
1066 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001067 // if user has not given the service enabled value
1068 // then revert it to the same state as it was
1069 // before.
1070 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1071 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001072 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001073
Ed Tanous10cb44f2024-04-11 13:05:20 -07001074 if (input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001075 {
1076 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001077 *input.remoteRoleMapData);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001078 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001079 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001080}
1081
Ed Tanous492ec932024-12-09 15:42:19 -08001082struct UserUpdateParams
1083{
1084 std::string username;
1085 std::optional<std::string> password;
1086 std::optional<bool> enabled;
1087 std::optional<std::string> roleId;
1088 std::optional<bool> locked;
1089 std::optional<std::vector<std::string>> accountTypes;
1090 bool userSelf;
1091 std::shared_ptr<persistent_data::UserSession> session;
1092 std::string dbusObjectPath;
1093};
1094
1095inline void
1096 afterVerifyUserExists(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1097 const UserUpdateParams& params, int rc)
1098{
1099 if (rc <= 0)
1100 {
1101 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1102 params.username);
1103 return;
1104 }
1105
1106 if (params.password)
1107 {
1108 int retval = pamUpdatePassword(params.username, *params.password);
1109
1110 if (retval == PAM_USER_UNKNOWN)
1111 {
1112 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1113 params.username);
1114 }
1115 else if (retval == PAM_AUTHTOK_ERR)
1116 {
1117 // If password is invalid
1118 messages::propertyValueFormatError(asyncResp->res, nullptr,
1119 "Password");
1120 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
1121 }
1122 else if (retval != PAM_SUCCESS)
1123 {
1124 messages::internalError(asyncResp->res);
1125 return;
1126 }
1127 else
1128 {
1129 // Remove existing sessions of the user when password
1130 // changed
1131 persistent_data::SessionStore::getInstance()
1132 .removeSessionsByUsernameExceptSession(params.username,
1133 params.session);
1134 messages::success(asyncResp->res);
1135 }
1136 }
1137
1138 if (params.enabled)
1139 {
1140 setDbusProperty(
1141 asyncResp, "Enabled", "xyz.openbmc_project.User.Manager",
1142 params.dbusObjectPath, "xyz.openbmc_project.User.Attributes",
1143 "UserEnabled", *params.enabled);
1144 }
1145
1146 if (params.roleId)
1147 {
1148 std::string priv = getPrivilegeFromRoleId(*params.roleId);
1149 if (priv.empty())
1150 {
1151 messages::propertyValueNotInList(asyncResp->res, true, "Locked");
1152 return;
1153 }
1154 setDbusProperty(asyncResp, "RoleId", "xyz.openbmc_project.User.Manager",
1155 params.dbusObjectPath,
1156 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1157 priv);
1158 }
1159
1160 if (params.locked)
1161 {
1162 // admin can unlock the account which is locked by
1163 // successive authentication failures but admin should
1164 // not be allowed to lock an account.
1165 if (*params.locked)
1166 {
1167 messages::propertyValueNotInList(asyncResp->res, "true", "Locked");
1168 return;
1169 }
1170 setDbusProperty(asyncResp, "Locked", "xyz.openbmc_project.User.Manager",
1171 params.dbusObjectPath,
1172 "xyz.openbmc_project.User.Attributes",
1173 "UserLockedForFailedAttempt", *params.locked);
1174 }
1175
1176 if (params.accountTypes)
1177 {
1178 patchAccountTypes(*params.accountTypes, asyncResp,
1179 params.dbusObjectPath, params.userSelf);
1180 }
1181}
1182
Abhishek Patel58345852022-02-02 08:54:25 -06001183inline void updateUserProperties(
Ed Tanous492ec932024-12-09 15:42:19 -08001184 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1185 const std::string& username, const std::optional<std::string>& password,
Abhishek Patel58345852022-02-02 08:54:25 -06001186 const std::optional<bool>& enabled,
1187 const std::optional<std::string>& roleId, const std::optional<bool>& locked,
Ed Tanous492ec932024-12-09 15:42:19 -08001188 const std::optional<std::vector<std::string>>& accountTypes, bool userSelf,
Ravi Tejae518ef32024-05-16 10:33:08 -05001189 const std::shared_ptr<persistent_data::UserSession>& session)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001190{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301191 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1192 tempObjPath /= username;
1193 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001194
Ed Tanous492ec932024-12-09 15:42:19 -08001195 UserUpdateParams params{username, password, enabled,
1196 roleId, locked, accountTypes,
1197 userSelf, session, dbusObjectPath};
1198
Ed Tanous6c51eab2021-06-03 12:30:29 -07001199 dbus::utility::checkDbusPathExists(
Ravi Tejae518ef32024-05-16 10:33:08 -05001200 dbusObjectPath,
Ed Tanous492ec932024-12-09 15:42:19 -08001201 std::bind_front(afterVerifyUserExists, asyncResp, std::move(params)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001202}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001203
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001204inline void handleAccountServiceHead(
1205 App& app, const crow::Request& req,
1206 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001207{
1208 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1209 {
1210 return;
1211 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001212 asyncResp->res.addHeader(
1213 boost::beast::http::field::link,
1214 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1215}
1216
1217inline void
Ed Tanous1aa375b2024-04-13 11:51:10 -07001218 getClientCertificates(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1219 const nlohmann::json::json_pointer& keyLocation)
1220{
1221 boost::urls::url url(
1222 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates");
1223 std::array<std::string_view, 1> interfaces = {
1224 "xyz.openbmc_project.Certs.Certificate"};
1225 std::string path = "/xyz/openbmc_project/certs/authority/truststore";
1226
1227 collection_util::getCollectionToKey(asyncResp, url, interfaces, path,
1228 keyLocation);
1229}
1230
1231inline void handleAccountServiceClientCertificatesInstanceHead(
1232 App& app, const crow::Request& req,
1233 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1234 const std::string& /*id*/)
1235{
1236 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1237 {
1238 return;
1239 }
1240
1241 asyncResp->res.addHeader(
1242 boost::beast::http::field::link,
1243 "</redfish/v1/JsonSchemas/Certificate/Certificate.json>; rel=describedby");
1244}
1245
1246inline void handleAccountServiceClientCertificatesInstanceGet(
1247 App& app, const crow::Request& req,
1248 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1249{
1250 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1251 {
1252 return;
1253 }
1254 BMCWEB_LOG_DEBUG("ClientCertificate Certificate ID={}", id);
1255 const boost::urls::url certURL = boost::urls::format(
1256 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/{}",
1257 id);
1258 std::string objPath =
1259 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1260 getCertificateProperties(
1261 asyncResp, objPath,
1262 "xyz.openbmc_project.Certs.Manager.Authority.Truststore", id, certURL,
1263 "Client Certificate");
1264}
1265
1266inline void handleAccountServiceClientCertificatesHead(
1267 App& app, const crow::Request& req,
1268 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1269{
1270 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1271 {
1272 return;
1273 }
1274
1275 asyncResp->res.addHeader(
1276 boost::beast::http::field::link,
1277 "</redfish/v1/JsonSchemas/CertificateCollection/CertificateCollection.json>; rel=describedby");
1278}
1279
1280inline void handleAccountServiceClientCertificatesGet(
1281 App& app, const crow::Request& req,
1282 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1283{
1284 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1285 {
1286 return;
1287 }
1288 getClientCertificates(asyncResp, "/Members"_json_pointer);
1289}
1290
Ed Tanous3ce36882024-06-09 10:58:16 -07001291using account_service::CertificateMappingAttribute;
1292using persistent_data::MTLSCommonNameParseMode;
1293inline CertificateMappingAttribute
1294 getCertificateMapping(MTLSCommonNameParseMode parse)
1295{
1296 switch (parse)
1297 {
1298 case MTLSCommonNameParseMode::CommonName:
1299 {
1300 return CertificateMappingAttribute::CommonName;
1301 }
1302 break;
1303 case MTLSCommonNameParseMode::Whole:
1304 {
1305 return CertificateMappingAttribute::Whole;
1306 }
1307 break;
1308 case MTLSCommonNameParseMode::UserPrincipalName:
1309 {
1310 return CertificateMappingAttribute::UserPrincipalName;
1311 }
1312 break;
1313
1314 case MTLSCommonNameParseMode::Meta:
1315 {
1316 if constexpr (BMCWEB_META_TLS_COMMON_NAME_PARSING)
1317 {
1318 return CertificateMappingAttribute::CommonName;
1319 }
1320 }
1321 break;
1322 default:
1323 {
1324 return CertificateMappingAttribute::Invalid;
1325 }
1326 break;
1327 }
1328}
1329
Ed Tanous1aa375b2024-04-13 11:51:10 -07001330inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001331 handleAccountServiceGet(App& app, const crow::Request& req,
1332 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1333{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001334 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1335 {
1336 return;
1337 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001338
1339 if (req.session == nullptr)
1340 {
1341 messages::internalError(asyncResp->res);
1342 return;
1343 }
1344
Ed Tanousc1019822024-03-06 12:54:38 -08001345 const persistent_data::AuthConfigMethods& authMethodsConfig =
1346 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1347
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001348 asyncResp->res.addHeader(
1349 boost::beast::http::field::link,
1350 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1351
Ed Tanous1ef4c342022-05-12 16:12:36 -07001352 nlohmann::json& json = asyncResp->res.jsonValue;
1353 json["@odata.id"] = "/redfish/v1/AccountService";
Ravi Teja482a69e2024-04-22 06:56:13 -05001354 json["@odata.type"] = "#AccountService.v1_15_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001355 json["Id"] = "AccountService";
1356 json["Name"] = "Account Service";
1357 json["Description"] = "Account Service";
1358 json["ServiceEnabled"] = true;
1359 json["MaxPasswordLength"] = 20;
1360 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1361 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
Ravi Teja482a69e2024-04-22 06:56:13 -05001362 json["HTTPBasicAuth"] = authMethodsConfig.basic
1363 ? account_service::BasicAuthState::Enabled
1364 : account_service::BasicAuthState::Disabled;
1365
1366 nlohmann::json::array_t allowed;
1367 allowed.emplace_back(account_service::BasicAuthState::Enabled);
1368 allowed.emplace_back(account_service::BasicAuthState::Disabled);
1369 json["HTTPBasicAuth@AllowableValues"] = std::move(allowed);
1370
Ed Tanous1aa375b2024-04-13 11:51:10 -07001371 nlohmann::json::object_t clientCertificate;
1372 clientCertificate["Enabled"] = authMethodsConfig.tls;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001373 clientCertificate["RespondToUnauthenticatedClients"] =
1374 !authMethodsConfig.tlsStrict;
Ed Tanous3ce36882024-06-09 10:58:16 -07001375
1376 using account_service::CertificateMappingAttribute;
1377
1378 CertificateMappingAttribute mapping =
1379 getCertificateMapping(authMethodsConfig.mTLSCommonNameParsingMode);
1380 if (mapping == CertificateMappingAttribute::Invalid)
1381 {
1382 messages::internalError(asyncResp->res);
1383 }
1384 else
1385 {
1386 clientCertificate["CertificateMappingAttribute"] = mapping;
1387 }
Ed Tanous1aa375b2024-04-13 11:51:10 -07001388 nlohmann::json::object_t certificates;
1389 certificates["@odata.id"] =
1390 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates";
1391 certificates["@odata.type"] =
1392 "#CertificateCollection.CertificateCollection";
1393 clientCertificate["Certificates"] = std::move(certificates);
1394 json["MultiFactorAuth"]["ClientCertificate"] = std::move(clientCertificate);
1395
1396 getClientCertificates(
1397 asyncResp,
1398 "/MultiFactorAuth/ClientCertificate/Certificates/Members"_json_pointer);
1399
Ed Tanous1ef4c342022-05-12 16:12:36 -07001400 json["Oem"]["OpenBMC"]["@odata.type"] =
Ed Tanous5b5574a2022-09-26 19:53:36 -07001401 "#OpenBMCAccountService.v1_0_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001402 json["Oem"]["OpenBMC"]["@odata.id"] =
1403 "/redfish/v1/AccountService#/Oem/OpenBMC";
1404 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1405 authMethodsConfig.basic;
1406 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1407 authMethodsConfig.sessionToken;
1408 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1409 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1410 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1411
1412 // /redfish/v1/AccountService/LDAP/Certificates is something only
1413 // ConfigureManager can access then only display when the user has
1414 // permissions ConfigureManager
1415 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001416 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001417
1418 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1419 effectiveUserPrivileges))
1420 {
1421 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1422 "/redfish/v1/AccountService/LDAP/Certificates";
1423 }
Ed Tanousdeae6a72024-11-11 21:58:57 -08001424 dbus::utility::getAllProperties(
1425 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1426 "xyz.openbmc_project.User.AccountPolicy",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001427 [asyncResp](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001428 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001429 if (ec)
1430 {
1431 messages::internalError(asyncResp->res);
1432 return;
1433 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001434
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001435 BMCWEB_LOG_DEBUG("Got {} properties for AccountService",
1436 propertiesList.size());
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001437
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001438 const uint8_t* minPasswordLength = nullptr;
1439 const uint32_t* accountUnlockTimeout = nullptr;
1440 const uint16_t* maxLoginAttemptBeforeLockout = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001441
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001442 const bool success = sdbusplus::unpackPropertiesNoThrow(
1443 dbus_utils::UnpackErrorPrinter(), propertiesList,
1444 "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout",
1445 accountUnlockTimeout, "MaxLoginAttemptBeforeLockout",
1446 maxLoginAttemptBeforeLockout);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001447
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001448 if (!success)
1449 {
1450 messages::internalError(asyncResp->res);
1451 return;
1452 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001453
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001454 if (minPasswordLength != nullptr)
1455 {
1456 asyncResp->res.jsonValue["MinPasswordLength"] =
1457 *minPasswordLength;
1458 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001459
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001460 if (accountUnlockTimeout != nullptr)
1461 {
1462 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1463 *accountUnlockTimeout;
1464 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001465
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001466 if (maxLoginAttemptBeforeLockout != nullptr)
1467 {
1468 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1469 *maxLoginAttemptBeforeLockout;
1470 }
1471 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001472
Ed Tanous02cad962022-06-30 16:50:15 -07001473 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001474 const std::string& ldapType) {
1475 if (!success)
1476 {
1477 return;
1478 }
1479 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1480 };
1481
1482 getLDAPConfigData("LDAP", callback);
1483 getLDAPConfigData("ActiveDirectory", callback);
1484}
1485
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001486inline void handleCertificateMappingAttributePatch(
1487 crow::Response& res, const std::string& certMapAttribute)
Ed Tanous3ce36882024-06-09 10:58:16 -07001488{
1489 MTLSCommonNameParseMode parseMode =
1490 persistent_data::getMTLSCommonNameParseMode(certMapAttribute);
1491 if (parseMode == MTLSCommonNameParseMode::Invalid)
1492 {
1493 messages::propertyValueNotInList(res, "CertificateMappingAttribute",
1494 certMapAttribute);
1495 return;
1496 }
1497
1498 persistent_data::AuthConfigMethods& authMethodsConfig =
1499 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1500 authMethodsConfig.mTLSCommonNameParsingMode = parseMode;
1501}
1502
Ed Tanous3281bcf2024-06-25 16:02:05 -07001503inline void handleRespondToUnauthenticatedClientsPatch(
1504 App& app, const crow::Request& req, crow::Response& res,
1505 bool respondToUnauthenticatedClients)
1506{
1507 if (req.session != nullptr)
1508 {
1509 // Sanity check. If the user isn't currently authenticated with mutual
1510 // TLS, they very likely are about to permanently lock themselves out.
1511 // Make sure they're using mutual TLS before allowing locking.
1512 if (req.session->sessionType != persistent_data::SessionType::MutualTLS)
1513 {
1514 messages::propertyValueExternalConflict(
1515 res,
1516 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
1517 respondToUnauthenticatedClients);
1518 return;
1519 }
1520 }
1521
1522 persistent_data::AuthConfigMethods& authMethodsConfig =
1523 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1524
1525 // Change the settings
1526 authMethodsConfig.tlsStrict = !respondToUnauthenticatedClients;
1527
1528 // Write settings to disk
1529 persistent_data::getConfig().writeData();
1530
1531 // Trigger a reload, to apply the new settings to new connections
1532 app.loadCertificate();
1533}
1534
Ed Tanous1ef4c342022-05-12 16:12:36 -07001535inline void handleAccountServicePatch(
1536 App& app, const crow::Request& req,
1537 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1538{
1539 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1540 {
1541 return;
1542 }
1543 std::optional<uint32_t> unlockTimeout;
1544 std::optional<uint16_t> lockoutThreshold;
1545 std::optional<uint8_t> minPasswordLength;
1546 std::optional<uint16_t> maxPasswordLength;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001547 LdapPatchParams ldapObject;
Ed Tanous3ce36882024-06-09 10:58:16 -07001548 std::optional<std::string> certificateMappingAttribute;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001549 std::optional<bool> respondToUnauthenticatedClients;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001550 LdapPatchParams activeDirectoryObject;
Ed Tanousc1019822024-03-06 12:54:38 -08001551 AuthMethods auth;
Ravi Teja482a69e2024-04-22 06:56:13 -05001552 std::optional<std::string> httpBasicAuth;
Ed Tanous3ce36882024-06-09 10:58:16 -07001553
Myung Baeafc474a2024-10-09 00:53:29 -07001554 if (!json_util::readJsonPatch( //
1555 req, asyncResp->res, //
1556 "AccountLockoutDuration", unlockTimeout, //
1557 "AccountLockoutThreshold", lockoutThreshold, //
1558 "ActiveDirectory/Authentication/AuthenticationType",
1559 activeDirectoryObject.authType, //
1560 "ActiveDirectory/Authentication/Password",
1561 activeDirectoryObject.password, //
1562 "ActiveDirectory/Authentication/Username",
1563 activeDirectoryObject.userName, //
1564 "ActiveDirectory/LDAPService/SearchSettings/BaseDistinguishedNames",
1565 activeDirectoryObject.baseDNList, //
1566 "ActiveDirectory/LDAPService/SearchSettings/GroupsAttribute",
1567 activeDirectoryObject.groupsAttribute, //
1568 "ActiveDirectory/LDAPService/SearchSettings/UsernameAttribute",
1569 activeDirectoryObject.userNameAttribute, //
1570 "ActiveDirectory/RemoteRoleMapping",
1571 activeDirectoryObject.remoteRoleMapData, //
1572 "ActiveDirectory/ServiceAddresses",
1573 activeDirectoryObject.serviceAddressList, //
1574 "ActiveDirectory/ServiceEnabled",
1575 activeDirectoryObject.serviceEnabled, //
1576 "HTTPBasicAuth", httpBasicAuth, //
1577 "LDAP/Authentication/AuthenticationType", ldapObject.authType, //
1578 "LDAP/Authentication/Password", ldapObject.password, //
1579 "LDAP/Authentication/Username", ldapObject.userName, //
1580 "LDAP/LDAPService/SearchSettings/BaseDistinguishedNames",
1581 ldapObject.baseDNList, //
1582 "LDAP/LDAPService/SearchSettings/GroupsAttribute",
1583 ldapObject.groupsAttribute, //
1584 "LDAP/LDAPService/SearchSettings/UsernameAttribute",
1585 ldapObject.userNameAttribute, //
1586 "LDAP/RemoteRoleMapping", ldapObject.remoteRoleMapData, //
1587 "LDAP/ServiceAddresses", ldapObject.serviceAddressList, //
1588 "LDAP/ServiceEnabled", ldapObject.serviceEnabled, //
1589 "MaxPasswordLength", maxPasswordLength, //
1590 "MinPasswordLength", minPasswordLength, //
1591 "MultiFactorAuth/ClientCertificate/CertificateMappingAttribute",
1592 certificateMappingAttribute, //
1593 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
1594 respondToUnauthenticatedClients, //
1595 "Oem/OpenBMC/AuthMethods/BasicAuth", auth.basicAuth, //
1596 "Oem/OpenBMC/AuthMethods/Cookie", auth.cookie, //
1597 "Oem/OpenBMC/AuthMethods/SessionToken", auth.sessionToken, //
1598 "Oem/OpenBMC/AuthMethods/TLS", auth.tls, //
1599 "Oem/OpenBMC/AuthMethods/XToken", auth.xToken //
1600 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001601 {
1602 return;
1603 }
1604
Ravi Teja482a69e2024-04-22 06:56:13 -05001605 if (httpBasicAuth)
1606 {
1607 if (*httpBasicAuth == "Enabled")
1608 {
1609 auth.basicAuth = true;
1610 }
1611 else if (*httpBasicAuth == "Disabled")
1612 {
1613 auth.basicAuth = false;
1614 }
1615 else
1616 {
1617 messages::propertyValueNotInList(asyncResp->res, "HttpBasicAuth",
1618 *httpBasicAuth);
1619 }
1620 }
1621
Ed Tanous3281bcf2024-06-25 16:02:05 -07001622 if (respondToUnauthenticatedClients)
1623 {
1624 handleRespondToUnauthenticatedClientsPatch(
1625 app, req, asyncResp->res, *respondToUnauthenticatedClients);
1626 }
1627
Ed Tanous3ce36882024-06-09 10:58:16 -07001628 if (certificateMappingAttribute)
1629 {
1630 handleCertificateMappingAttributePatch(asyncResp->res,
1631 *certificateMappingAttribute);
1632 }
1633
Ed Tanous1ef4c342022-05-12 16:12:36 -07001634 if (minPasswordLength)
1635 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001636 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301637 asyncResp, "MinPasswordLength", "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001638 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001639 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
Ginu Georgee93abac2024-06-14 17:35:27 +05301640 *minPasswordLength);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001641 }
1642
1643 if (maxPasswordLength)
1644 {
1645 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1646 }
1647
Ed Tanous10cb44f2024-04-11 13:05:20 -07001648 handleLDAPPatch(std::move(activeDirectoryObject), asyncResp,
1649 "ActiveDirectory");
1650 handleLDAPPatch(std::move(ldapObject), asyncResp, "LDAP");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001651
Ed Tanousc1019822024-03-06 12:54:38 -08001652 handleAuthMethodsPatch(asyncResp, auth);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001653
Ed Tanous1ef4c342022-05-12 16:12:36 -07001654 if (unlockTimeout)
1655 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001656 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301657 asyncResp, "AccountLockoutDuration",
1658 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001659 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
Ed Tanous1ef4c342022-05-12 16:12:36 -07001660 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
Ginu Georgee93abac2024-06-14 17:35:27 +05301661 *unlockTimeout);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001662 }
1663 if (lockoutThreshold)
1664 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001665 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301666 asyncResp, "AccountLockoutThreshold",
1667 "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",
Ginu Georgee93abac2024-06-14 17:35:27 +05301670 "MaxLoginAttemptBeforeLockout", *lockoutThreshold);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001671 }
1672}
1673
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001674inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001675 App& app, const crow::Request& req,
1676 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1677{
1678 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1679 {
1680 return;
1681 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001682 asyncResp->res.addHeader(
1683 boost::beast::http::field::link,
1684 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1685}
1686
1687inline void handleAccountCollectionGet(
1688 App& app, const crow::Request& req,
1689 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1690{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001691 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1692 {
1693 return;
1694 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001695
1696 if (req.session == nullptr)
1697 {
1698 messages::internalError(asyncResp->res);
1699 return;
1700 }
1701
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001702 asyncResp->res.addHeader(
1703 boost::beast::http::field::link,
1704 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001705
1706 asyncResp->res.jsonValue["@odata.id"] =
1707 "/redfish/v1/AccountService/Accounts";
1708 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1709 "ManagerAccountCollection";
1710 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1711 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1712
1713 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001714 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001715
1716 std::string thisUser;
1717 if (req.session)
1718 {
1719 thisUser = req.session->username;
1720 }
George Liu5eb468d2023-06-20 17:03:24 +08001721 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
1722 dbus::utility::getManagedObjects(
1723 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001724 [asyncResp, thisUser, effectiveUserPrivileges](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001725 const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001726 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001727 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001728 {
1729 messages::internalError(asyncResp->res);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001730 return;
1731 }
1732
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001733 bool userCanSeeAllAccounts =
1734 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1735
1736 bool userCanSeeSelf =
1737 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1738
1739 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1740 memberArray = nlohmann::json::array();
1741
1742 for (const auto& userpath : users)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001743 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001744 std::string user = userpath.first.filename();
1745 if (user.empty())
1746 {
1747 messages::internalError(asyncResp->res);
1748 BMCWEB_LOG_ERROR("Invalid firmware ID");
1749
1750 return;
1751 }
1752
1753 // As clarified by Redfish here:
1754 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1755 // Users without ConfigureUsers, only see their own
1756 // account. Users with ConfigureUsers, see all
1757 // accounts.
1758 if (userCanSeeAllAccounts ||
1759 (thisUser == user && userCanSeeSelf))
1760 {
1761 nlohmann::json::object_t member;
1762 member["@odata.id"] = boost::urls::format(
1763 "/redfish/v1/AccountService/Accounts/{}", user);
1764 memberArray.emplace_back(std::move(member));
1765 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001766 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001767 asyncResp->res.jsonValue["Members@odata.count"] =
1768 memberArray.size();
1769 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001770}
1771
Ninad Palsule97e90da2023-05-17 14:04:52 -05001772inline void processAfterCreateUser(
1773 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1774 const std::string& username, const std::string& password,
1775 const boost::system::error_code& ec, sdbusplus::message_t& m)
1776{
1777 if (ec)
1778 {
1779 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1780 return;
1781 }
1782
1783 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1784 {
1785 // At this point we have a user that's been
1786 // created, but the password set
1787 // failed.Something is wrong, so delete the user
1788 // that we've already created
1789 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1790 tempObjPath /= username;
1791 const std::string userPath(tempObjPath);
1792
1793 crow::connections::systemBus->async_method_call(
1794 [asyncResp, password](const boost::system::error_code& ec3) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001795 if (ec3)
1796 {
1797 messages::internalError(asyncResp->res);
1798 return;
1799 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001800
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001801 // If password is invalid
1802 messages::propertyValueFormatError(asyncResp->res, nullptr,
1803 "Password");
1804 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001805 "xyz.openbmc_project.User.Manager", userPath,
1806 "xyz.openbmc_project.Object.Delete", "Delete");
1807
Ed Tanous62598e32023-07-17 17:06:25 -07001808 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
Ninad Palsule97e90da2023-05-17 14:04:52 -05001809 return;
1810 }
1811
1812 messages::created(asyncResp->res);
1813 asyncResp->res.addHeader("Location",
1814 "/redfish/v1/AccountService/Accounts/" + username);
1815}
1816
1817inline void processAfterGetAllGroups(
1818 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1819 const std::string& username, const std::string& password,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001820 const std::string& roleId, bool enabled,
Ninad Palsule9ba73932023-06-01 16:38:57 -05001821 std::optional<std::vector<std::string>> accountTypes,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001822 const std::vector<std::string>& allGroupsList)
Ninad Palsule97e90da2023-05-17 14:04:52 -05001823{
Ninad Palsule3e72c202023-03-27 17:19:55 -05001824 std::vector<std::string> userGroups;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001825 std::vector<std::string> accountTypeUserGroups;
1826
1827 // If user specified account types then convert them to unix user groups
1828 if (accountTypes)
1829 {
1830 if (!getUserGroupFromAccountType(asyncResp->res, *accountTypes,
1831 accountTypeUserGroups))
1832 {
1833 // Problem in mapping Account Types to User Groups, Error already
1834 // logged.
1835 return;
1836 }
1837 }
1838
Ninad Palsule3e72c202023-03-27 17:19:55 -05001839 for (const auto& grp : allGroupsList)
1840 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001841 // If user specified the account type then only accept groups which are
1842 // in the account types group list.
1843 if (!accountTypeUserGroups.empty())
1844 {
1845 bool found = false;
1846 for (const auto& grp1 : accountTypeUserGroups)
1847 {
1848 if (grp == grp1)
1849 {
1850 found = true;
1851 break;
1852 }
1853 }
1854 if (!found)
1855 {
1856 continue;
1857 }
1858 }
1859
Ninad Palsule3e72c202023-03-27 17:19:55 -05001860 // Console access is provided to the user who is a member of
1861 // hostconsole group and has a administrator role. So, set
1862 // hostconsole group only for the administrator.
Ninad Palsule9ba73932023-06-01 16:38:57 -05001863 if ((grp == "hostconsole") && (roleId != "priv-admin"))
Ninad Palsule3e72c202023-03-27 17:19:55 -05001864 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001865 if (!accountTypeUserGroups.empty())
1866 {
Ed Tanous62598e32023-07-17 17:06:25 -07001867 BMCWEB_LOG_ERROR(
1868 "Only administrator can get HostConsole access");
Ninad Palsule9ba73932023-06-01 16:38:57 -05001869 asyncResp->res.result(boost::beast::http::status::bad_request);
1870 return;
1871 }
1872 continue;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001873 }
Ninad Palsule9ba73932023-06-01 16:38:57 -05001874 userGroups.emplace_back(grp);
1875 }
1876
1877 // Make sure user specified groups are valid. This is internal error because
1878 // it some inconsistencies between user manager and bmcweb.
1879 if (!accountTypeUserGroups.empty() &&
1880 accountTypeUserGroups.size() != userGroups.size())
1881 {
1882 messages::internalError(asyncResp->res);
1883 return;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001884 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001885 crow::connections::systemBus->async_method_call(
1886 [asyncResp, username, password](const boost::system::error_code& ec2,
1887 sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001888 processAfterCreateUser(asyncResp, username, password, ec2, m);
1889 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001890 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ninad Palsule3e72c202023-03-27 17:19:55 -05001891 "xyz.openbmc_project.User.Manager", "CreateUser", username, userGroups,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001892 roleId, enabled);
Ninad Palsule97e90da2023-05-17 14:04:52 -05001893}
1894
Ed Tanous1ef4c342022-05-12 16:12:36 -07001895inline void handleAccountCollectionPost(
1896 App& app, const crow::Request& req,
1897 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1898{
1899 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1900 {
1901 return;
1902 }
1903 std::string username;
1904 std::string password;
Ed Tanouse01d0c32023-06-30 13:21:32 -07001905 std::optional<std::string> roleIdJson;
1906 std::optional<bool> enabledJson;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001907 std::optional<std::vector<std::string>> accountTypes;
Myung Baeafc474a2024-10-09 00:53:29 -07001908 if (!json_util::readJsonPatch( //
1909 req, asyncResp->res, //
1910 "AccountTypes", accountTypes, //
1911 "Enabled", enabledJson, //
1912 "Password", password, //
1913 "RoleId", roleIdJson, //
1914 "UserName", username //
1915 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001916 {
1917 return;
1918 }
1919
Ed Tanouse01d0c32023-06-30 13:21:32 -07001920 std::string roleId = roleIdJson.value_or("User");
1921 std::string priv = getPrivilegeFromRoleId(roleId);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001922 if (priv.empty())
1923 {
Ed Tanouse01d0c32023-06-30 13:21:32 -07001924 messages::propertyValueNotInList(asyncResp->res, roleId, "RoleId");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001925 return;
1926 }
Asmitha Karunanithi239adf82022-03-25 02:59:03 -05001927 roleId = priv;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001928
Ed Tanouse01d0c32023-06-30 13:21:32 -07001929 bool enabled = enabledJson.value_or(true);
1930
Ed Tanous1ef4c342022-05-12 16:12:36 -07001931 // Reading AllGroups property
Ed Tanousdeae6a72024-11-11 21:58:57 -08001932 dbus::utility::getProperty<std::vector<std::string>>(
1933 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1934 "xyz.openbmc_project.User.Manager", "AllGroups",
Ninad Palsule9ba73932023-06-01 16:38:57 -05001935 [asyncResp, username, password{std::move(password)}, roleId, enabled,
1936 accountTypes](const boost::system::error_code& ec,
1937 const std::vector<std::string>& allGroupsList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001938 if (ec)
1939 {
Gunnar Millsa0735a42024-09-06 12:51:11 -05001940 BMCWEB_LOG_ERROR("D-Bus response error {}", ec);
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001941 messages::internalError(asyncResp->res);
1942 return;
1943 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001944
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001945 if (allGroupsList.empty())
1946 {
1947 messages::internalError(asyncResp->res);
1948 return;
1949 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001950
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001951 processAfterGetAllGroups(asyncResp, username, password, roleId,
1952 enabled, accountTypes, allGroupsList);
1953 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001954}
1955
1956inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001957 handleAccountHead(App& app, const crow::Request& req,
1958 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1959 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001960{
1961 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1962 {
1963 return;
1964 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001965 asyncResp->res.addHeader(
1966 boost::beast::http::field::link,
1967 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1968}
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001969
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001970inline void
1971 handleAccountGet(App& app, const crow::Request& req,
1972 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1973 const std::string& accountName)
1974{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001975 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1976 {
1977 return;
1978 }
1979 asyncResp->res.addHeader(
1980 boost::beast::http::field::link,
1981 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1982
Ed Tanous25b54db2024-04-17 15:40:31 -07001983 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
1984 {
1985 // If authentication is disabled, there are no user accounts
1986 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1987 accountName);
1988 return;
1989 }
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001990
Ed Tanous1ef4c342022-05-12 16:12:36 -07001991 if (req.session == nullptr)
1992 {
1993 messages::internalError(asyncResp->res);
1994 return;
1995 }
1996 if (req.session->username != accountName)
1997 {
1998 // At this point we've determined that the user is trying to
1999 // modify a user that isn't them. We need to verify that they
2000 // have permissions to modify other users, so re-run the auth
2001 // check with the same permissions, minus ConfigureSelf.
2002 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002003 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002004 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
2005 "ConfigureManager"};
2006 if (!effectiveUserPrivileges.isSupersetOf(
2007 requiredPermissionsToChangeNonSelf))
2008 {
Ed Tanous62598e32023-07-17 17:06:25 -07002009 BMCWEB_LOG_DEBUG("GET Account denied access");
Ed Tanous1ef4c342022-05-12 16:12:36 -07002010 messages::insufficientPrivilege(asyncResp->res);
2011 return;
2012 }
2013 }
2014
George Liu5eb468d2023-06-20 17:03:24 +08002015 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
2016 dbus::utility::getManagedObjects(
2017 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07002018 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002019 accountName](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07002020 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002021 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002022 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002023 messages::internalError(asyncResp->res);
2024 return;
2025 }
2026 const auto userIt = std::ranges::find_if(
2027 users,
2028 [accountName](
2029 const std::pair<sdbusplus::message::object_path,
2030 dbus::utility::DBusInterfacesMap>& user) {
2031 return accountName == user.first.filename();
2032 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002033
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002034 if (userIt == users.end())
2035 {
2036 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2037 accountName);
2038 return;
2039 }
2040
2041 asyncResp->res.jsonValue["@odata.type"] =
2042 "#ManagerAccount.v1_7_0.ManagerAccount";
2043 asyncResp->res.jsonValue["Name"] = "User Account";
2044 asyncResp->res.jsonValue["Description"] = "User Account";
2045 asyncResp->res.jsonValue["Password"] = nullptr;
2046 asyncResp->res.jsonValue["StrictAccountTypes"] = true;
2047
2048 for (const auto& interface : userIt->second)
2049 {
2050 if (interface.first == "xyz.openbmc_project.User.Attributes")
2051 {
Asmitha Karunanithib437a532024-12-20 04:54:25 -06002052 const bool* userEnabled = nullptr;
2053 const bool* userLocked = nullptr;
2054 const std::string* userPrivPtr = nullptr;
2055 const bool* userPasswordExpired = nullptr;
2056 const std::vector<std::string>* userGroups = nullptr;
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002057
Asmitha Karunanithib437a532024-12-20 04:54:25 -06002058 const bool success = sdbusplus::unpackPropertiesNoThrow(
2059 dbus_utils::UnpackErrorPrinter(), interface.second,
2060 "UserEnabled", userEnabled,
2061 "UserLockedForFailedAttempt", userLocked,
2062 "UserPrivilege", userPrivPtr, "UserPasswordExpired",
2063 userPasswordExpired, "UserGroups", userGroups);
2064 if (!success)
2065 {
2066 messages::internalError(asyncResp->res);
2067 return;
2068 }
2069 if (userEnabled == nullptr)
2070 {
2071 BMCWEB_LOG_ERROR("UserEnabled wasn't a bool");
2072 messages::internalError(asyncResp->res);
2073 return;
2074 }
2075 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
2076
2077 if (userLocked == nullptr)
2078 {
2079 BMCWEB_LOG_ERROR("UserLockedForF"
2080 "ailedAttempt "
2081 "wasn't a bool");
2082 messages::internalError(asyncResp->res);
2083 return;
2084 }
2085 asyncResp->res.jsonValue["Locked"] = *userLocked;
2086 nlohmann::json::array_t allowed;
2087 // can only unlock accounts
2088 allowed.emplace_back("false");
2089 asyncResp->res.jsonValue["Locked@Redfish.AllowableValues"] =
2090 std::move(allowed);
2091
2092 if (userPrivPtr == nullptr)
2093 {
2094 BMCWEB_LOG_ERROR("UserPrivilege wasn't a "
2095 "string");
2096 messages::internalError(asyncResp->res);
2097 return;
2098 }
2099 std::string role = getRoleIdFromPrivilege(*userPrivPtr);
2100 if (role.empty())
2101 {
2102 BMCWEB_LOG_ERROR("Invalid user role");
2103 messages::internalError(asyncResp->res);
2104 return;
2105 }
2106 asyncResp->res.jsonValue["RoleId"] = role;
2107
2108 nlohmann::json& roleEntry =
2109 asyncResp->res.jsonValue["Links"]["Role"];
2110 roleEntry["@odata.id"] = boost::urls::format(
2111 "/redfish/v1/AccountService/Roles/{}", role);
2112
2113 if (userPasswordExpired == nullptr)
2114 {
2115 BMCWEB_LOG_ERROR("UserPasswordExpired wasn't a bool");
2116 messages::internalError(asyncResp->res);
2117 return;
2118 }
2119 asyncResp->res.jsonValue["PasswordChangeRequired"] =
2120 *userPasswordExpired;
2121
2122 if (userGroups == nullptr)
2123 {
2124 BMCWEB_LOG_ERROR("userGroups wasn't a string vector");
2125 messages::internalError(asyncResp->res);
2126 return;
2127 }
2128 if (!translateUserGroup(*userGroups, asyncResp->res))
2129 {
2130 BMCWEB_LOG_ERROR("userGroups mapping failed");
2131 messages::internalError(asyncResp->res);
2132 return;
Abhishek Patelc7229812022-02-01 10:07:15 -06002133 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002134 }
2135 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002136
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002137 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
2138 "/redfish/v1/AccountService/Accounts/{}", accountName);
2139 asyncResp->res.jsonValue["Id"] = accountName;
2140 asyncResp->res.jsonValue["UserName"] = accountName;
2141 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002142}
2143
2144inline void
Gunnar Mills20fc3072023-01-27 15:13:36 -06002145 handleAccountDelete(App& app, const crow::Request& req,
2146 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2147 const std::string& username)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002148{
2149 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2150 {
2151 return;
2152 }
2153
Ed Tanous25b54db2024-04-17 15:40:31 -07002154 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2155 {
2156 // If authentication is disabled, there are no user accounts
2157 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2158 return;
2159 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002160 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
2161 tempObjPath /= username;
2162 const std::string userPath(tempObjPath);
2163
2164 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002165 [asyncResp, username](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002166 if (ec)
2167 {
2168 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2169 username);
2170 return;
2171 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002172
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002173 messages::accountRemoved(asyncResp->res);
2174 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002175 "xyz.openbmc_project.User.Manager", userPath,
2176 "xyz.openbmc_project.Object.Delete", "Delete");
2177}
2178
2179inline void
2180 handleAccountPatch(App& app, const crow::Request& req,
2181 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2182 const std::string& username)
2183{
2184 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2185 {
2186 return;
2187 }
Ed Tanous25b54db2024-04-17 15:40:31 -07002188 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2189 {
2190 // If authentication is disabled, there are no user accounts
2191 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2192 return;
2193 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002194 std::optional<std::string> newUserName;
2195 std::optional<std::string> password;
2196 std::optional<bool> enabled;
2197 std::optional<std::string> roleId;
2198 std::optional<bool> locked;
Abhishek Patel58345852022-02-02 08:54:25 -06002199 std::optional<std::vector<std::string>> accountTypes;
2200
Ed Tanous1ef4c342022-05-12 16:12:36 -07002201 if (req.session == nullptr)
2202 {
2203 messages::internalError(asyncResp->res);
2204 return;
2205 }
2206
Ed Tanous2b9c1df2024-04-06 13:52:01 -07002207 bool userSelf = (username == req.session->username);
2208
Ed Tanous1ef4c342022-05-12 16:12:36 -07002209 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002210 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002211 Privileges configureUsers = {"ConfigureUsers"};
2212 bool userHasConfigureUsers =
2213 effectiveUserPrivileges.isSupersetOf(configureUsers);
2214 if (userHasConfigureUsers)
2215 {
2216 // Users with ConfigureUsers can modify for all users
Myung Baeafc474a2024-10-09 00:53:29 -07002217 if (!json_util::readJsonPatch( //
2218 req, asyncResp->res, //
2219 "AccountTypes", accountTypes, //
2220 "Enabled", enabled, //
2221 "Locked", locked, //
2222 "Password", password, //
2223 "RoleId", roleId, //
2224 "UserName", newUserName //
2225 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07002226 {
2227 return;
2228 }
2229 }
2230 else
2231 {
2232 // ConfigureSelf accounts can only modify their own account
Abhishek Patel58345852022-02-02 08:54:25 -06002233 if (!userSelf)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002234 {
2235 messages::insufficientPrivilege(asyncResp->res);
2236 return;
2237 }
2238
2239 // ConfigureSelf accounts can only modify their password
2240 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
2241 password))
2242 {
2243 return;
2244 }
2245 }
2246
2247 // if user name is not provided in the patch method or if it
2248 // matches the user name in the URI, then we are treating it as
2249 // updating user properties other then username. If username
2250 // provided doesn't match the URI, then we are treating this as
2251 // user rename request.
2252 if (!newUserName || (newUserName.value() == username))
2253 {
2254 updateUserProperties(asyncResp, username, password, enabled, roleId,
Ravi Tejae518ef32024-05-16 10:33:08 -05002255 locked, accountTypes, userSelf, req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002256 return;
2257 }
2258 crow::connections::systemBus->async_method_call(
2259 [asyncResp, username, password(std::move(password)),
2260 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Ravi Tejae518ef32024-05-16 10:33:08 -05002261 locked, userSelf, req, accountTypes(std::move(accountTypes))](
Ed Tanouse81de512023-06-27 17:07:00 -07002262 const boost::system::error_code& ec, sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002263 if (ec)
2264 {
2265 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
2266 username);
2267 return;
2268 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002269
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002270 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
2271 locked, accountTypes, userSelf, req.session);
2272 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002273 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
2274 "xyz.openbmc_project.User.Manager", "RenameUser", username,
2275 *newUserName);
2276}
2277
Ed Tanous6c51eab2021-06-03 12:30:29 -07002278inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07002279{
Ed Tanous6c51eab2021-06-03 12:30:29 -07002280 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002281 .privileges(redfish::privileges::headAccountService)
2282 .methods(boost::beast::http::verb::head)(
2283 std::bind_front(handleAccountServiceHead, std::ref(app)));
2284
2285 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07002286 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002287 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002288 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002289
Ed Tanousf5ffd802021-07-19 10:55:33 -07002290 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06002291 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07002292 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002293 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07002294
Ed Tanous1aa375b2024-04-13 11:51:10 -07002295 BMCWEB_ROUTE(
2296 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002297 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002298 .privileges(redfish::privileges::headCertificateCollection)
2299 .methods(boost::beast::http::verb::head)(std::bind_front(
2300 handleAccountServiceClientCertificatesHead, std::ref(app)));
2301
2302 BMCWEB_ROUTE(
2303 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002304 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002305 .privileges(redfish::privileges::getCertificateCollection)
2306 .methods(boost::beast::http::verb::get)(std::bind_front(
2307 handleAccountServiceClientCertificatesGet, std::ref(app)));
2308
2309 BMCWEB_ROUTE(
2310 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002311 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002312 .privileges(redfish::privileges::headCertificate)
2313 .methods(boost::beast::http::verb::head)(std::bind_front(
2314 handleAccountServiceClientCertificatesInstanceHead, std::ref(app)));
2315
2316 BMCWEB_ROUTE(
2317 app,
2318 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
2319 .privileges(redfish::privileges::getCertificate)
2320 .methods(boost::beast::http::verb::get)(std::bind_front(
2321 handleAccountServiceClientCertificatesInstanceGet, std::ref(app)));
2322
Ed Tanous6c51eab2021-06-03 12:30:29 -07002323 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002324 .privileges(redfish::privileges::headManagerAccountCollection)
2325 .methods(boost::beast::http::verb::head)(
2326 std::bind_front(handleAccountCollectionHead, std::ref(app)));
2327
2328 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002329 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002330 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002331 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07002332
Ed Tanous6c51eab2021-06-03 12:30:29 -07002333 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002334 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002335 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002336 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07002337
2338 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002339 .privileges(redfish::privileges::headManagerAccount)
2340 .methods(boost::beast::http::verb::head)(
2341 std::bind_front(handleAccountHead, std::ref(app)));
2342
2343 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07002344 .privileges(redfish::privileges::getManagerAccount)
2345 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002346 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002347
2348 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002349 // TODO this privilege should be using the generated endpoints, but
2350 // because of the special handling of ConfigureSelf, it's not able to
2351 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07002352 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
2353 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002354 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002355
2356 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002357 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002358 .methods(boost::beast::http::verb::delete_)(
Gunnar Mills20fc3072023-01-27 15:13:36 -06002359 std::bind_front(handleAccountDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002360}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002361
Ed Tanous1abe55e2018-09-05 08:30:59 -07002362} // namespace redfish