blob: c61fa4670693c60f863f5ad21fa0fd752e0bd5d5 [file] [log] [blame]
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010017
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080018#include "app.hpp"
19#include "dbus_utility.hpp"
20#include "error_messages.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070021#include "generated/enums/account_service.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080022#include "openbmc_dbus_rest.hpp"
23#include "persistent_data.hpp"
24#include "query.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070025#include "registries/privilege_registry.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080026#include "utils/dbus_utils.hpp"
27#include "utils/json_utils.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070028
Jonathan Doman1e1e5982021-06-11 09:36:17 -070029#include <sdbusplus/asio/property.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020030#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050031
George Liu2b731192023-01-11 16:27:13 +080032#include <array>
Abhishek Patelc7229812022-02-01 10:07:15 -060033#include <optional>
34#include <string>
George Liu2b731192023-01-11 16:27:13 +080035#include <string_view>
Abhishek Patelc7229812022-02-01 10:07:15 -060036#include <vector>
37
Ed Tanous1abe55e2018-09-05 08:30:59 -070038namespace redfish
39{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010040
Ed Tanous23a21a12020-07-25 04:45:05 +000041constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053042 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070043constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053044 "/xyz/openbmc_project/user/ldap/active_directory";
45
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053046constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053047constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
48constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
49constexpr const char* ldapConfigInterface =
50 "xyz.openbmc_project.User.Ldap.Config";
51constexpr const char* ldapCreateInterface =
52 "xyz.openbmc_project.User.Ldap.Create";
53constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053054constexpr const char* ldapPrivMapperInterface =
55 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053056constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
57constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
Ratan Gupta6973a582018-12-13 18:25:44 +053058
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060059struct LDAPRoleMapData
60{
61 std::string groupName;
62 std::string privilege;
63};
64
Ratan Gupta6973a582018-12-13 18:25:44 +053065struct LDAPConfigData
66{
67 std::string uri{};
68 std::string bindDN{};
69 std::string baseDN{};
70 std::string searchScope{};
71 std::string serverType{};
72 bool serviceEnabled = false;
73 std::string userNameAttribute{};
74 std::string groupAttribute{};
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060075 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053076};
77
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060078inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053079{
80 if (role == "priv-admin")
81 {
82 return "Administrator";
83 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070084 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053085 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053086 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053087 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070088 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053089 {
90 return "Operator";
91 }
92 return "";
93}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060094inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053095{
96 if (role == "Administrator")
97 {
98 return "priv-admin";
99 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700100 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530101 {
102 return "priv-user";
103 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700104 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530105 {
106 return "priv-operator";
107 }
108 return "";
109}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700110
Abhishek Patelc7229812022-02-01 10:07:15 -0600111/**
112 * @brief Maps user group names retrieved from D-Bus object to
113 * Account Types.
114 *
115 * @param[in] userGroups List of User groups
116 * @param[out] res AccountTypes populated
117 *
118 * @return true in case of success, false if UserGroups contains
119 * invalid group name(s).
120 */
121inline bool translateUserGroup(const std::vector<std::string>& userGroups,
122 crow::Response& res)
123{
124 std::vector<std::string> accountTypes;
125 for (const auto& userGroup : userGroups)
126 {
127 if (userGroup == "redfish")
128 {
129 accountTypes.emplace_back("Redfish");
130 accountTypes.emplace_back("WebUI");
131 }
132 else if (userGroup == "ipmi")
133 {
134 accountTypes.emplace_back("IPMI");
135 }
136 else if (userGroup == "ssh")
137 {
Abhishek Patelc7229812022-02-01 10:07:15 -0600138 accountTypes.emplace_back("ManagerConsole");
139 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500140 else if (userGroup == "hostconsole")
141 {
142 // The hostconsole group controls who can access the host console
143 // port via ssh and websocket.
144 accountTypes.emplace_back("HostConsole");
145 }
Abhishek Patelc7229812022-02-01 10:07:15 -0600146 else if (userGroup == "web")
147 {
148 // 'web' is one of the valid groups in the UserGroups property of
149 // the user account in the D-Bus object. This group is currently not
150 // doing anything, and is considered to be equivalent to 'redfish'.
151 // 'redfish' user group is mapped to 'Redfish'and 'WebUI'
152 // AccountTypes, so do nothing here...
153 }
154 else
155 {
156 // Invalid user group name. Caller throws an excption.
157 return false;
158 }
159 }
160
161 res.jsonValue["AccountTypes"] = std::move(accountTypes);
162 return true;
163}
164
zhanghch058d1b46d2021-04-01 11:18:24 +0800165inline void userErrorMessageHandler(
166 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
167 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000168{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000169 if (e == nullptr)
170 {
171 messages::internalError(asyncResp->res);
172 return;
173 }
174
Manojkiran Eda055806b2020-11-03 09:36:28 +0530175 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000176 if (strcmp(errorMessage,
177 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
178 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800179 messages::resourceAlreadyExists(asyncResp->res, "ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000180 "UserName", newUser);
181 }
182 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
183 "UserNameDoesNotExist") == 0)
184 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800185 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000186 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700187 else if ((strcmp(errorMessage,
188 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
189 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800190 (strcmp(
191 errorMessage,
192 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
193 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000194 {
195 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
196 }
197 else if (strcmp(errorMessage,
198 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
199 {
200 messages::createLimitReachedForResource(asyncResp->res);
201 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000202 else
203 {
204 messages::internalError(asyncResp->res);
205 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000206}
207
Ed Tanous81ce6092020-12-17 16:54:55 +0000208inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000209 const LDAPConfigData& confData,
210 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530211{
Patrick Williams89492a12023-05-10 07:51:34 -0500212 std::string service = (ldapType == "LDAP") ? "LDAPService"
213 : "ActiveDirectoryService";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600214
Ed Tanous14766872022-03-15 10:44:42 -0700215 nlohmann::json& ldap = jsonResponse[ldapType];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600216
Ed Tanous14766872022-03-15 10:44:42 -0700217 ldap["ServiceEnabled"] = confData.serviceEnabled;
218 ldap["ServiceAddresses"] = nlohmann::json::array({confData.uri});
Ed Tanous0ec8b832022-03-14 14:56:47 -0700219 ldap["Authentication"]["AuthenticationType"] =
220 account_service::AuthenticationTypes::UsernameAndPassword;
Ed Tanous14766872022-03-15 10:44:42 -0700221 ldap["Authentication"]["Username"] = confData.bindDN;
222 ldap["Authentication"]["Password"] = nullptr;
223
224 ldap["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"] =
225 nlohmann::json::array({confData.baseDN});
226 ldap["LDAPService"]["SearchSettings"]["UsernameAttribute"] =
227 confData.userNameAttribute;
228 ldap["LDAPService"]["SearchSettings"]["GroupsAttribute"] =
229 confData.groupAttribute;
230
231 nlohmann::json& roleMapArray = ldap["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600232 roleMapArray = nlohmann::json::array();
Ed Tanous9eb808c2022-01-25 10:19:23 -0800233 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600234 {
235 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
236 << obj.second.groupName << "\n";
Ed Tanous613dabe2022-07-09 11:17:36 -0700237
Ed Tanous613dabe2022-07-09 11:17:36 -0700238 nlohmann::json::object_t remoteGroup;
239 remoteGroup["RemoteGroup"] = obj.second.groupName;
Jorge Cisneros329f0342022-11-04 16:26:25 +0000240 remoteGroup["LocalRole"] = getRoleIdFromPrivilege(obj.second.privilege);
241 roleMapArray.emplace_back(std::move(remoteGroup));
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600242 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530243}
244
245/**
Ratan Gupta06785242019-07-26 22:30:16 +0530246 * @brief validates given JSON input and then calls appropriate method to
247 * create, to delete or to set Rolemapping object based on the given input.
248 *
249 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000250inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800251 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530252 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700253 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530254{
255 for (size_t index = 0; index < input.size(); index++)
256 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700257 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530258
259 if (thisJson.is_null())
260 {
261 // delete the existing object
262 if (index < roleMapObjData.size())
263 {
264 crow::connections::systemBus->async_method_call(
265 [asyncResp, roleMapObjData, serverType,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800266 index](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700267 if (ec)
268 {
269 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
270 messages::internalError(asyncResp->res);
271 return;
272 }
Patrick Williams89492a12023-05-10 07:51:34 -0500273 asyncResp->res.jsonValue[serverType]["RemoteRoleMapping"]
274 [index] = nullptr;
Ratan Gupta06785242019-07-26 22:30:16 +0530275 },
276 ldapDbusService, roleMapObjData[index].first,
277 "xyz.openbmc_project.Object.Delete", "Delete");
278 }
279 else
280 {
281 BMCWEB_LOG_ERROR << "Can't delete the object";
282 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800283 asyncResp->res,
284 thisJson.dump(2, ' ', true,
285 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530286 "RemoteRoleMapping/" + std::to_string(index));
287 return;
288 }
289 }
290 else if (thisJson.empty())
291 {
292 // Don't do anything for the empty objects,parse next json
293 // eg {"RemoteRoleMapping",[{}]}
294 }
295 else
296 {
297 // update/create the object
298 std::optional<std::string> remoteGroup;
299 std::optional<std::string> localRole;
300
Ed Tanousf23b7292020-10-15 09:41:17 -0700301 // This is a copy, but it's required in this case because of how
302 // readJson is structured
303 nlohmann::json thisJsonCopy = thisJson;
304 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
305 "RemoteGroup", remoteGroup, "LocalRole",
306 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530307 {
308 continue;
309 }
310
311 // Update existing RoleMapping Object
312 if (index < roleMapObjData.size())
313 {
314 BMCWEB_LOG_DEBUG << "Update Role Map Object";
315 // If "RemoteGroup" info is provided
316 if (remoteGroup)
317 {
318 crow::connections::systemBus->async_method_call(
319 [asyncResp, roleMapObjData, serverType, index,
Ravi Teja25e055a2022-08-09 03:18:38 -0500320 remoteGroup](const boost::system::error_code& ec,
321 const sdbusplus::message::message& msg) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700322 if (ec)
323 {
Ravi Teja25e055a2022-08-09 03:18:38 -0500324 const sd_bus_error* dbusError = msg.get_error();
325 if ((dbusError != nullptr) &&
326 (dbusError->name ==
327 std::string_view(
328 "xyz.openbmc_project.Common.Error.InvalidArgument")))
329 {
330 BMCWEB_LOG_WARNING << "DBUS response error: "
331 << ec;
332 messages::propertyValueIncorrect(asyncResp->res,
333 "RemoteGroup",
334 *remoteGroup);
335 return;
336 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700337 messages::internalError(asyncResp->res);
338 return;
339 }
340 asyncResp->res
341 .jsonValue[serverType]["RemoteRoleMapping"][index]
342 ["RemoteGroup"] = *remoteGroup;
Ratan Gupta06785242019-07-26 22:30:16 +0530343 },
344 ldapDbusService, roleMapObjData[index].first,
345 propertyInterface, "Set",
346 "xyz.openbmc_project.User.PrivilegeMapperEntry",
347 "GroupName",
Ed Tanous168e20c2021-12-13 14:39:53 -0800348 dbus::utility::DbusVariantType(
349 std::move(*remoteGroup)));
Ratan Gupta06785242019-07-26 22:30:16 +0530350 }
351
352 // If "LocalRole" info is provided
353 if (localRole)
354 {
355 crow::connections::systemBus->async_method_call(
356 [asyncResp, roleMapObjData, serverType, index,
Ravi Teja25e055a2022-08-09 03:18:38 -0500357 localRole](const boost::system::error_code& ec,
358 const sdbusplus::message::message& msg) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700359 if (ec)
360 {
Ravi Teja25e055a2022-08-09 03:18:38 -0500361 const sd_bus_error* dbusError = msg.get_error();
362 if ((dbusError != nullptr) &&
363 (dbusError->name ==
364 std::string_view(
365 "xyz.openbmc_project.Common.Error.InvalidArgument")))
366 {
367 BMCWEB_LOG_WARNING << "DBUS response error: "
368 << ec;
369 messages::propertyValueIncorrect(
370 asyncResp->res, "LocalRole", *localRole);
371 return;
372 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700373 messages::internalError(asyncResp->res);
374 return;
375 }
376 asyncResp->res
377 .jsonValue[serverType]["RemoteRoleMapping"][index]
378 ["LocalRole"] = *localRole;
Ratan Gupta06785242019-07-26 22:30:16 +0530379 },
380 ldapDbusService, roleMapObjData[index].first,
381 propertyInterface, "Set",
382 "xyz.openbmc_project.User.PrivilegeMapperEntry",
383 "Privilege",
Ed Tanous168e20c2021-12-13 14:39:53 -0800384 dbus::utility::DbusVariantType(
Ratan Gupta06785242019-07-26 22:30:16 +0530385 getPrivilegeFromRoleId(std::move(*localRole))));
386 }
387 }
388 // Create a new RoleMapping Object.
389 else
390 {
391 BMCWEB_LOG_DEBUG
392 << "setRoleMappingProperties: Creating new Object";
Patrick Williams89492a12023-05-10 07:51:34 -0500393 std::string pathString = "RemoteRoleMapping/" +
394 std::to_string(index);
Ratan Gupta06785242019-07-26 22:30:16 +0530395
396 if (!localRole)
397 {
398 messages::propertyMissing(asyncResp->res,
399 pathString + "/LocalRole");
400 continue;
401 }
402 if (!remoteGroup)
403 {
404 messages::propertyMissing(asyncResp->res,
405 pathString + "/RemoteGroup");
406 continue;
407 }
408
409 std::string dbusObjectPath;
410 if (serverType == "ActiveDirectory")
411 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700412 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530413 }
414 else if (serverType == "LDAP")
415 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000416 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530417 }
418
419 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
420 << ",LocalRole=" << *localRole;
421
422 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700423 [asyncResp, serverType, localRole,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800424 remoteGroup](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700425 if (ec)
426 {
427 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
428 messages::internalError(asyncResp->res);
429 return;
430 }
431 nlohmann::json& remoteRoleJson =
432 asyncResp->res
433 .jsonValue[serverType]["RemoteRoleMapping"];
434 nlohmann::json::object_t roleMapEntry;
435 roleMapEntry["LocalRole"] = *localRole;
436 roleMapEntry["RemoteGroup"] = *remoteGroup;
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500437 remoteRoleJson.emplace_back(std::move(roleMapEntry));
Ratan Gupta06785242019-07-26 22:30:16 +0530438 },
439 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700440 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530441 getPrivilegeFromRoleId(std::move(*localRole)));
442 }
443 }
444 }
445}
446
447/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530448 * Function that retrieves all properties for LDAP config object
449 * into JSON
450 */
451template <typename CallbackFunc>
452inline void getLDAPConfigData(const std::string& ldapType,
453 CallbackFunc&& callback)
454{
George Liu2b731192023-01-11 16:27:13 +0800455 constexpr std::array<std::string_view, 2> interfaces = {
456 ldapEnableInterface, ldapConfigInterface};
Ratan Gupta6973a582018-12-13 18:25:44 +0530457
George Liu2b731192023-01-11 16:27:13 +0800458 dbus::utility::getDbusObject(
459 ldapConfigObjectName, interfaces,
460 [callback, ldapType](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800461 const dbus::utility::MapperGetObject& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700462 if (ec || resp.empty())
463 {
464 BMCWEB_LOG_ERROR
465 << "DBUS response error during getting of service name: " << ec;
466 LDAPConfigData empty{};
467 callback(false, empty, ldapType);
468 return;
469 }
470 std::string service = resp.begin()->first;
471 crow::connections::systemBus->async_method_call(
472 [callback,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800473 ldapType](const boost::system::error_code& errorCode,
Ed Tanous002d39b2022-05-31 08:59:27 -0700474 const dbus::utility::ManagedObjectType& ldapObjects) {
475 LDAPConfigData confData{};
476 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600477 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700478 callback(false, confData, ldapType);
479 BMCWEB_LOG_ERROR << "D-Bus responses error: " << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600480 return;
481 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600482
Ed Tanous002d39b2022-05-31 08:59:27 -0700483 std::string ldapDbusType;
484 std::string searchString;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600485
Ed Tanous002d39b2022-05-31 08:59:27 -0700486 if (ldapType == "LDAP")
487 {
488 ldapDbusType =
489 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
490 searchString = "openldap";
491 }
492 else if (ldapType == "ActiveDirectory")
493 {
494 ldapDbusType =
495 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
496 searchString = "active_directory";
497 }
498 else
499 {
500 BMCWEB_LOG_ERROR << "Can't get the DbusType for the given type="
501 << ldapType;
502 callback(false, confData, ldapType);
503 return;
504 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600505
Ed Tanous002d39b2022-05-31 08:59:27 -0700506 std::string ldapEnableInterfaceStr = ldapEnableInterface;
507 std::string ldapConfigInterfaceStr = ldapConfigInterface;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600508
Ed Tanous002d39b2022-05-31 08:59:27 -0700509 for (const auto& object : ldapObjects)
510 {
511 // let's find the object whose ldap type is equal to the
512 // given type
513 if (object.first.str.find(searchString) == std::string::npos)
514 {
515 continue;
516 }
517
518 for (const auto& interface : object.second)
519 {
520 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600521 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700522 // rest of the properties are string.
523 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600524 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700525 if (property.first == "Enabled")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600526 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700527 const bool* value =
528 std::get_if<bool>(&property.second);
529 if (value == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600530 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700531 continue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600532 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700533 confData.serviceEnabled = *value;
534 break;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600535 }
536 }
537 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700538 else if (interface.first == ldapConfigInterfaceStr)
539 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700540 for (const auto& property : interface.second)
541 {
542 const std::string* strValue =
543 std::get_if<std::string>(&property.second);
544 if (strValue == nullptr)
545 {
546 continue;
547 }
548 if (property.first == "LDAPServerURI")
549 {
550 confData.uri = *strValue;
551 }
552 else if (property.first == "LDAPBindDN")
553 {
554 confData.bindDN = *strValue;
555 }
556 else if (property.first == "LDAPBaseDN")
557 {
558 confData.baseDN = *strValue;
559 }
560 else if (property.first == "LDAPSearchScope")
561 {
562 confData.searchScope = *strValue;
563 }
564 else if (property.first == "GroupNameAttribute")
565 {
566 confData.groupAttribute = *strValue;
567 }
568 else if (property.first == "UserNameAttribute")
569 {
570 confData.userNameAttribute = *strValue;
571 }
572 else if (property.first == "LDAPType")
573 {
574 confData.serverType = *strValue;
575 }
576 }
577 }
578 else if (interface.first ==
579 "xyz.openbmc_project.User.PrivilegeMapperEntry")
580 {
581 LDAPRoleMapData roleMapData{};
582 for (const auto& property : interface.second)
583 {
584 const std::string* strValue =
585 std::get_if<std::string>(&property.second);
586
587 if (strValue == nullptr)
588 {
589 continue;
590 }
591
592 if (property.first == "GroupName")
593 {
594 roleMapData.groupName = *strValue;
595 }
596 else if (property.first == "Privilege")
597 {
598 roleMapData.privilege = *strValue;
599 }
600 }
601
602 confData.groupRoleList.emplace_back(object.first.str,
603 roleMapData);
604 }
605 }
606 }
607 callback(true, confData, ldapType);
608 },
609 service, ldapRootObject, dbusObjManagerIntf, "GetManagedObjects");
George Liu2b731192023-01-11 16:27:13 +0800610 });
Ratan Gupta6973a582018-12-13 18:25:44 +0530611}
612
Ed Tanous6c51eab2021-06-03 12:30:29 -0700613/**
614 * @brief parses the authentication section under the LDAP
615 * @param input JSON data
616 * @param asyncResp pointer to the JSON response
617 * @param userName userName to be filled from the given JSON.
618 * @param password password to be filled from the given JSON.
619 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700620inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700621 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
622 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700624 std::optional<std::string> authType;
625
626 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
627 authType, "Username", username, "Password",
628 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700630 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700632 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530633 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700634 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530635 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700636 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530637 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700638 messages::propertyValueNotInList(asyncResp->res, *authType,
639 "AuthenticationType");
640 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530641 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700642}
643/**
644 * @brief parses the LDAPService section under the LDAP
645 * @param input JSON data
646 * @param asyncResp pointer to the JSON response
647 * @param baseDNList baseDN to be filled from the given JSON.
648 * @param userNameAttribute userName to be filled from the given JSON.
649 * @param groupaAttribute password to be filled from the given JSON.
650 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530651
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700652inline void
653 parseLDAPServiceJson(nlohmann::json input,
654 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
655 std::optional<std::vector<std::string>>& baseDNList,
656 std::optional<std::string>& userNameAttribute,
657 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700658{
659 std::optional<nlohmann::json> searchSettings;
660
661 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
662 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530663 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700664 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530665 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700666 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530667 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700668 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530669 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700670 if (!json_util::readJson(*searchSettings, asyncResp->res,
671 "BaseDistinguishedNames", baseDNList,
672 "UsernameAttribute", userNameAttribute,
673 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530674 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700675 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530676 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700677}
678/**
679 * @brief updates the LDAP server address and updates the
680 json response with the new value.
681 * @param serviceAddressList address to be updated.
682 * @param asyncResp pointer to the JSON response
683 * @param ldapServerElementName Type of LDAP
684 server(openLDAP/ActiveDirectory)
685 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530686
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700687inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700688 const std::vector<std::string>& serviceAddressList,
689 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
690 const std::string& ldapServerElementName,
691 const std::string& ldapConfigObject)
692{
693 crow::connections::systemBus->async_method_call(
694 [asyncResp, ldapServerElementName,
Ravi Teja25e055a2022-08-09 03:18:38 -0500695 serviceAddressList](const boost::system::error_code& ec,
696 sdbusplus::message::message& msg) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700697 if (ec)
698 {
Ravi Teja25e055a2022-08-09 03:18:38 -0500699 const sd_bus_error* dbusError = msg.get_error();
700 if ((dbusError != nullptr) &&
701 (dbusError->name ==
702 std::string_view(
703 "xyz.openbmc_project.Common.Error.InvalidArgument")))
704 {
705 BMCWEB_LOG_WARNING
706 << "Error Occurred in updating the service address";
707 messages::propertyValueIncorrect(asyncResp->res,
708 "ServiceAddresses",
709 serviceAddressList.front());
710 return;
711 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700712 messages::internalError(asyncResp->res);
713 return;
714 }
715 std::vector<std::string> modifiedserviceAddressList = {
716 serviceAddressList.front()};
717 asyncResp->res.jsonValue[ldapServerElementName]["ServiceAddresses"] =
718 modifiedserviceAddressList;
719 if ((serviceAddressList).size() > 1)
720 {
721 messages::propertyValueModified(asyncResp->res, "ServiceAddresses",
722 serviceAddressList.front());
723 }
724 BMCWEB_LOG_DEBUG << "Updated the service address";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700725 },
726 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
727 ldapConfigInterface, "LDAPServerURI",
Ed Tanous168e20c2021-12-13 14:39:53 -0800728 dbus::utility::DbusVariantType(serviceAddressList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700729}
730/**
731 * @brief updates the LDAP Bind DN and updates the
732 json response with the new value.
733 * @param username name of the user which needs to be updated.
734 * @param asyncResp pointer to the JSON response
735 * @param ldapServerElementName Type of LDAP
736 server(openLDAP/ActiveDirectory)
737 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530738
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700739inline void
740 handleUserNamePatch(const std::string& username,
741 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
742 const std::string& ldapServerElementName,
743 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700744{
745 crow::connections::systemBus->async_method_call(
746 [asyncResp, username,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800747 ldapServerElementName](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700748 if (ec)
749 {
750 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
751 messages::internalError(asyncResp->res);
752 return;
753 }
Patrick Williams89492a12023-05-10 07:51:34 -0500754 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
755 ["Username"] = username;
Ed Tanous002d39b2022-05-31 08:59:27 -0700756 BMCWEB_LOG_DEBUG << "Updated the username";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700757 },
758 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800759 ldapConfigInterface, "LDAPBindDN",
760 dbus::utility::DbusVariantType(username));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700761}
762
763/**
764 * @brief updates the LDAP password
765 * @param password : ldap password which needs to be updated.
766 * @param asyncResp pointer to the JSON response
767 * @param ldapServerElementName Type of LDAP
768 * server(openLDAP/ActiveDirectory)
769 */
770
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700771inline void
772 handlePasswordPatch(const std::string& password,
773 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
774 const std::string& ldapServerElementName,
775 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700776{
777 crow::connections::systemBus->async_method_call(
778 [asyncResp, password,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800779 ldapServerElementName](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700780 if (ec)
781 {
782 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
783 messages::internalError(asyncResp->res);
784 return;
785 }
Patrick Williams89492a12023-05-10 07:51:34 -0500786 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
787 ["Password"] = "";
Ed Tanous002d39b2022-05-31 08:59:27 -0700788 BMCWEB_LOG_DEBUG << "Updated the password";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700789 },
790 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
791 ldapConfigInterface, "LDAPBindDNPassword",
Ed Tanous168e20c2021-12-13 14:39:53 -0800792 dbus::utility::DbusVariantType(password));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700793}
794
795/**
796 * @brief updates the LDAP BaseDN and updates the
797 json response with the new value.
798 * @param baseDNList baseDN list which needs to be updated.
799 * @param asyncResp pointer to the JSON response
800 * @param ldapServerElementName Type of LDAP
801 server(openLDAP/ActiveDirectory)
802 */
803
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700804inline void
805 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
806 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
807 const std::string& ldapServerElementName,
808 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700809{
810 crow::connections::systemBus->async_method_call(
811 [asyncResp, baseDNList,
Ravi Teja25e055a2022-08-09 03:18:38 -0500812 ldapServerElementName](const boost::system::error_code& ec,
813 const sdbusplus::message::message& msg) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700814 if (ec)
815 {
816 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
Ravi Teja25e055a2022-08-09 03:18:38 -0500817 const sd_bus_error* dbusError = msg.get_error();
818 if ((dbusError != nullptr) &&
819 (dbusError->name ==
820 std::string_view(
821 "xyz.openbmc_project.Common.Error.InvalidArgument")))
822 {
823 messages::propertyValueIncorrect(asyncResp->res,
824 "BaseDistinguishedNames",
825 baseDNList.front());
826 return;
827 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700828 messages::internalError(asyncResp->res);
829 return;
830 }
831 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
832 auto& searchSettingsJson =
833 serverTypeJson["LDAPService"]["SearchSettings"];
834 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
835 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
836 if (baseDNList.size() > 1)
837 {
838 messages::propertyValueModified(
839 asyncResp->res, "BaseDistinguishedNames", baseDNList.front());
840 }
841 BMCWEB_LOG_DEBUG << "Updated the base DN";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700842 },
843 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
844 ldapConfigInterface, "LDAPBaseDN",
Ed Tanous168e20c2021-12-13 14:39:53 -0800845 dbus::utility::DbusVariantType(baseDNList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700846}
847/**
848 * @brief updates the LDAP user name attribute and updates the
849 json response with the new value.
850 * @param userNameAttribute attribute to be updated.
851 * @param asyncResp pointer to the JSON response
852 * @param ldapServerElementName Type of LDAP
853 server(openLDAP/ActiveDirectory)
854 */
855
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700856inline void
857 handleUserNameAttrPatch(const std::string& userNameAttribute,
858 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
859 const std::string& ldapServerElementName,
860 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700861{
862 crow::connections::systemBus->async_method_call(
863 [asyncResp, userNameAttribute,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800864 ldapServerElementName](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700865 if (ec)
866 {
867 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
868 "username attribute";
869 messages::internalError(asyncResp->res);
870 return;
871 }
872 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
873 auto& searchSettingsJson =
874 serverTypeJson["LDAPService"]["SearchSettings"];
875 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
876 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700877 },
878 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
879 ldapConfigInterface, "UserNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800880 dbus::utility::DbusVariantType(userNameAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700881}
882/**
883 * @brief updates the LDAP group attribute and updates the
884 json response with the new value.
885 * @param groupsAttribute attribute to be updated.
886 * @param asyncResp pointer to the JSON response
887 * @param ldapServerElementName Type of LDAP
888 server(openLDAP/ActiveDirectory)
889 */
890
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700891inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700892 const std::string& groupsAttribute,
893 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
894 const std::string& ldapServerElementName,
895 const std::string& ldapConfigObject)
896{
897 crow::connections::systemBus->async_method_call(
898 [asyncResp, groupsAttribute,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800899 ldapServerElementName](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700900 if (ec)
901 {
902 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
903 "groupname attribute";
904 messages::internalError(asyncResp->res);
905 return;
906 }
907 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
908 auto& searchSettingsJson =
909 serverTypeJson["LDAPService"]["SearchSettings"];
910 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
911 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700912 },
913 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
914 ldapConfigInterface, "GroupNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800915 dbus::utility::DbusVariantType(groupsAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700916}
917/**
918 * @brief updates the LDAP service enable and updates the
919 json response with the new value.
920 * @param input JSON data.
921 * @param asyncResp pointer to the JSON response
922 * @param ldapServerElementName Type of LDAP
923 server(openLDAP/ActiveDirectory)
924 */
925
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700926inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700927 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
928 const std::string& ldapServerElementName,
929 const std::string& ldapConfigObject)
930{
931 crow::connections::systemBus->async_method_call(
932 [asyncResp, serviceEnabled,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800933 ldapServerElementName](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700934 if (ec)
935 {
936 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the service enable";
937 messages::internalError(asyncResp->res);
938 return;
939 }
940 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
941 serviceEnabled;
942 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700943 },
944 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800945 ldapEnableInterface, "Enabled",
946 dbus::utility::DbusVariantType(serviceEnabled));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700947}
948
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700949inline void
950 handleAuthMethodsPatch(nlohmann::json& input,
951 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700952{
953 std::optional<bool> basicAuth;
954 std::optional<bool> cookie;
955 std::optional<bool> sessionToken;
956 std::optional<bool> xToken;
957 std::optional<bool> tls;
958
959 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
960 "Cookie", cookie, "SessionToken", sessionToken,
961 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530962 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700963 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
964 return;
965 }
966
967 // Make a copy of methods configuration
968 persistent_data::AuthConfigMethods authMethodsConfig =
969 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
970
971 if (basicAuth)
972 {
973#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
974 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800975 asyncResp->res,
976 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700977 return;
978#endif
979 authMethodsConfig.basic = *basicAuth;
980 }
981
982 if (cookie)
983 {
984#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800985 messages::actionNotSupported(
986 asyncResp->res,
987 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700988 return;
989#endif
990 authMethodsConfig.cookie = *cookie;
991 }
992
993 if (sessionToken)
994 {
995#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
996 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800997 asyncResp->res,
998 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700999 return;
1000#endif
1001 authMethodsConfig.sessionToken = *sessionToken;
1002 }
1003
1004 if (xToken)
1005 {
1006#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +08001007 messages::actionNotSupported(
1008 asyncResp->res,
1009 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -07001010 return;
1011#endif
1012 authMethodsConfig.xtoken = *xToken;
1013 }
1014
1015 if (tls)
1016 {
1017#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +08001018 messages::actionNotSupported(
1019 asyncResp->res,
1020 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -07001021 return;
1022#endif
1023 authMethodsConfig.tls = *tls;
1024 }
1025
1026 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
1027 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
1028 !authMethodsConfig.tls)
1029 {
1030 // Do not allow user to disable everything
1031 messages::actionNotSupported(asyncResp->res,
1032 "of disabling all available methods");
1033 return;
1034 }
1035
1036 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
1037 authMethodsConfig);
1038 // Save configuration immediately
1039 persistent_data::getConfig().writeData();
1040
1041 messages::success(asyncResp->res);
1042}
1043
1044/**
1045 * @brief Get the required values from the given JSON, validates the
1046 * value and create the LDAP config object.
1047 * @param input JSON data
1048 * @param asyncResp pointer to the JSON response
1049 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
1050 */
1051
1052inline void handleLDAPPatch(nlohmann::json& input,
1053 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1054 const std::string& serverType)
1055{
1056 std::string dbusObjectPath;
1057 if (serverType == "ActiveDirectory")
1058 {
1059 dbusObjectPath = adConfigObject;
1060 }
1061 else if (serverType == "LDAP")
1062 {
1063 dbusObjectPath = ldapConfigObjectName;
1064 }
1065 else
1066 {
1067 return;
1068 }
1069
1070 std::optional<nlohmann::json> authentication;
1071 std::optional<nlohmann::json> ldapService;
1072 std::optional<std::vector<std::string>> serviceAddressList;
1073 std::optional<bool> serviceEnabled;
1074 std::optional<std::vector<std::string>> baseDNList;
1075 std::optional<std::string> userNameAttribute;
1076 std::optional<std::string> groupsAttribute;
1077 std::optional<std::string> userName;
1078 std::optional<std::string> password;
1079 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1080
1081 if (!json_util::readJson(input, asyncResp->res, "Authentication",
1082 authentication, "LDAPService", ldapService,
1083 "ServiceAddresses", serviceAddressList,
1084 "ServiceEnabled", serviceEnabled,
1085 "RemoteRoleMapping", remoteRoleMapData))
1086 {
1087 return;
1088 }
1089
1090 if (authentication)
1091 {
1092 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1093 password);
1094 }
1095 if (ldapService)
1096 {
1097 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1098 userNameAttribute, groupsAttribute);
1099 }
1100 if (serviceAddressList)
1101 {
Ed Tanous26f69762022-01-25 09:49:11 -08001102 if (serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301103 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001104 messages::propertyValueNotInList(asyncResp->res, "[]",
1105 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001106 return;
1107 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001108 }
1109 if (baseDNList)
1110 {
Ed Tanous26f69762022-01-25 09:49:11 -08001111 if (baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +05301112 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001113 messages::propertyValueNotInList(asyncResp->res, "[]",
1114 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301115 return;
1116 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001117 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301118
Ed Tanous6c51eab2021-06-03 12:30:29 -07001119 // nothing to update, then return
1120 if (!userName && !password && !serviceAddressList && !baseDNList &&
1121 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1122 !remoteRoleMapData)
1123 {
1124 return;
1125 }
1126
1127 // Get the existing resource first then keep modifying
1128 // whenever any property gets updated.
Ed Tanous002d39b2022-05-31 08:59:27 -07001129 getLDAPConfigData(
1130 serverType,
1131 [asyncResp, userName, password, baseDNList, userNameAttribute,
1132 groupsAttribute, serviceAddressList, serviceEnabled, dbusObjectPath,
1133 remoteRoleMapData](bool success, const LDAPConfigData& confData,
1134 const std::string& serverT) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001135 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301136 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001137 messages::internalError(asyncResp->res);
1138 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301139 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001140 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1141 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301142 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001143 // Disable the service first and update the rest of
1144 // the properties.
1145 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301146 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001147
Ratan Gupta8a07d282019-03-16 08:33:47 +05301148 if (serviceAddressList)
1149 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001150 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1151 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301152 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001153 if (userName)
1154 {
1155 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1156 }
1157 if (password)
1158 {
1159 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1160 }
1161
Ratan Gupta8a07d282019-03-16 08:33:47 +05301162 if (baseDNList)
1163 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001164 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1165 }
1166 if (userNameAttribute)
1167 {
1168 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1169 dbusObjectPath);
1170 }
1171 if (groupsAttribute)
1172 {
1173 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1174 dbusObjectPath);
1175 }
1176 if (serviceEnabled)
1177 {
1178 // if user has given the value as true then enable
1179 // the service. if user has given false then no-op
1180 // as service is already stopped.
1181 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301182 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001183 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1184 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301185 }
1186 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001187 else
1188 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001189 // if user has not given the service enabled value
1190 // then revert it to the same state as it was
1191 // before.
1192 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1193 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001194 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001195
Ed Tanous6c51eab2021-06-03 12:30:29 -07001196 if (remoteRoleMapData)
1197 {
1198 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1199 *remoteRoleMapData);
1200 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001201 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001202}
1203
1204inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1205 const std::string& username,
Ed Tanous618c14b2022-06-30 17:44:25 -07001206 const std::optional<std::string>& password,
1207 const std::optional<bool>& enabled,
1208 const std::optional<std::string>& roleId,
1209 const std::optional<bool>& locked)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001210{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301211 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1212 tempObjPath /= username;
1213 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001214
1215 dbus::utility::checkDbusPathExists(
Ed Tanous618c14b2022-06-30 17:44:25 -07001216 dbusObjectPath, [dbusObjectPath, username, password, roleId, enabled,
1217 locked, asyncResp{std::move(asyncResp)}](int rc) {
1218 if (rc <= 0)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001219 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001220 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1221 username);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001222 return;
1223 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001224
Ed Tanous618c14b2022-06-30 17:44:25 -07001225 if (password)
1226 {
1227 int retval = pamUpdatePassword(username, *password);
1228
1229 if (retval == PAM_USER_UNKNOWN)
1230 {
1231 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1232 username);
1233 }
1234 else if (retval == PAM_AUTHTOK_ERR)
1235 {
1236 // If password is invalid
1237 messages::propertyValueFormatError(asyncResp->res,
1238 *password, "Password");
1239 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1240 }
1241 else if (retval != PAM_SUCCESS)
1242 {
1243 messages::internalError(asyncResp->res);
1244 return;
1245 }
1246 else
1247 {
1248 messages::success(asyncResp->res);
1249 }
1250 }
1251
1252 if (enabled)
1253 {
1254 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001255 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001256 if (ec)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001257 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001258 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001259 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001260 return;
1261 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001262 messages::success(asyncResp->res);
1263 return;
Ed Tanous618c14b2022-06-30 17:44:25 -07001264 },
1265 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1266 "org.freedesktop.DBus.Properties", "Set",
1267 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1268 dbus::utility::DbusVariantType{*enabled});
Ed Tanous002d39b2022-05-31 08:59:27 -07001269 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001270
Ed Tanous618c14b2022-06-30 17:44:25 -07001271 if (roleId)
1272 {
1273 std::string priv = getPrivilegeFromRoleId(*roleId);
1274 if (priv.empty())
1275 {
1276 messages::propertyValueNotInList(asyncResp->res, *roleId,
1277 "RoleId");
1278 return;
1279 }
1280
1281 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001282 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001283 if (ec)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001284 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001285 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1286 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001287 return;
1288 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001289 messages::success(asyncResp->res);
Ed Tanous618c14b2022-06-30 17:44:25 -07001290 },
1291 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1292 "org.freedesktop.DBus.Properties", "Set",
1293 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1294 dbus::utility::DbusVariantType{priv});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001295 }
1296
Ed Tanous618c14b2022-06-30 17:44:25 -07001297 if (locked)
1298 {
1299 // admin can unlock the account which is locked by
1300 // successive authentication failures but admin should
1301 // not be allowed to lock an account.
1302 if (*locked)
1303 {
1304 messages::propertyValueNotInList(asyncResp->res, "true",
1305 "Locked");
1306 return;
1307 }
1308
1309 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001310 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001311 if (ec)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001312 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001313 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1314 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001315 return;
1316 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001317 messages::success(asyncResp->res);
1318 return;
Ed Tanous618c14b2022-06-30 17:44:25 -07001319 },
1320 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1321 "org.freedesktop.DBus.Properties", "Set",
1322 "xyz.openbmc_project.User.Attributes",
1323 "UserLockedForFailedAttempt",
1324 dbus::utility::DbusVariantType{*locked});
1325 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001326 });
1327}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001328
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001329inline void handleAccountServiceHead(
1330 App& app, const crow::Request& req,
1331 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001332{
1333 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1334 {
1335 return;
1336 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001337 asyncResp->res.addHeader(
1338 boost::beast::http::field::link,
1339 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1340}
1341
1342inline void
1343 handleAccountServiceGet(App& app, const crow::Request& req,
1344 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1345{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001346 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1347 {
1348 return;
1349 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001350
1351 if (req.session == nullptr)
1352 {
1353 messages::internalError(asyncResp->res);
1354 return;
1355 }
1356
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001357 asyncResp->res.addHeader(
1358 boost::beast::http::field::link,
1359 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1360
Ed Tanous1ef4c342022-05-12 16:12:36 -07001361 const persistent_data::AuthConfigMethods& authMethodsConfig =
1362 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1363
1364 nlohmann::json& json = asyncResp->res.jsonValue;
1365 json["@odata.id"] = "/redfish/v1/AccountService";
1366 json["@odata.type"] = "#AccountService."
1367 "v1_10_0.AccountService";
1368 json["Id"] = "AccountService";
1369 json["Name"] = "Account Service";
1370 json["Description"] = "Account Service";
1371 json["ServiceEnabled"] = true;
1372 json["MaxPasswordLength"] = 20;
1373 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1374 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
1375 json["Oem"]["OpenBMC"]["@odata.type"] =
Ed Tanous5b5574a2022-09-26 19:53:36 -07001376 "#OpenBMCAccountService.v1_0_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001377 json["Oem"]["OpenBMC"]["@odata.id"] =
1378 "/redfish/v1/AccountService#/Oem/OpenBMC";
1379 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1380 authMethodsConfig.basic;
1381 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1382 authMethodsConfig.sessionToken;
1383 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1384 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1385 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1386
1387 // /redfish/v1/AccountService/LDAP/Certificates is something only
1388 // ConfigureManager can access then only display when the user has
1389 // permissions ConfigureManager
1390 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001391 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001392
1393 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1394 effectiveUserPrivileges))
1395 {
1396 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1397 "/redfish/v1/AccountService/LDAP/Certificates";
1398 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001399 sdbusplus::asio::getAllProperties(
1400 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1401 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.AccountPolicy",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001402 [asyncResp](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001403 const dbus::utility::DBusPropertiesMap& propertiesList) {
1404 if (ec)
1405 {
1406 messages::internalError(asyncResp->res);
1407 return;
1408 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001409
Ed Tanous1ef4c342022-05-12 16:12:36 -07001410 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1411 << "properties for AccountService";
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001412
1413 const uint8_t* minPasswordLength = nullptr;
1414 const uint32_t* accountUnlockTimeout = nullptr;
1415 const uint16_t* maxLoginAttemptBeforeLockout = nullptr;
1416
1417 const bool success = sdbusplus::unpackPropertiesNoThrow(
1418 dbus_utils::UnpackErrorPrinter(), propertiesList,
1419 "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout",
1420 accountUnlockTimeout, "MaxLoginAttemptBeforeLockout",
1421 maxLoginAttemptBeforeLockout);
1422
1423 if (!success)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001424 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001425 messages::internalError(asyncResp->res);
1426 return;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001427 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001428
1429 if (minPasswordLength != nullptr)
1430 {
1431 asyncResp->res.jsonValue["MinPasswordLength"] = *minPasswordLength;
1432 }
1433
1434 if (accountUnlockTimeout != nullptr)
1435 {
1436 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1437 *accountUnlockTimeout;
1438 }
1439
1440 if (maxLoginAttemptBeforeLockout != nullptr)
1441 {
1442 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1443 *maxLoginAttemptBeforeLockout;
1444 }
1445 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001446
Ed Tanous02cad962022-06-30 16:50:15 -07001447 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001448 const std::string& ldapType) {
1449 if (!success)
1450 {
1451 return;
1452 }
1453 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1454 };
1455
1456 getLDAPConfigData("LDAP", callback);
1457 getLDAPConfigData("ActiveDirectory", callback);
1458}
1459
1460inline void handleAccountServicePatch(
1461 App& app, const crow::Request& req,
1462 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1463{
1464 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1465 {
1466 return;
1467 }
1468 std::optional<uint32_t> unlockTimeout;
1469 std::optional<uint16_t> lockoutThreshold;
1470 std::optional<uint8_t> minPasswordLength;
1471 std::optional<uint16_t> maxPasswordLength;
1472 std::optional<nlohmann::json> ldapObject;
1473 std::optional<nlohmann::json> activeDirectoryObject;
1474 std::optional<nlohmann::json> oemObject;
1475
1476 if (!json_util::readJsonPatch(
1477 req, asyncResp->res, "AccountLockoutDuration", unlockTimeout,
1478 "AccountLockoutThreshold", lockoutThreshold, "MaxPasswordLength",
1479 maxPasswordLength, "MinPasswordLength", minPasswordLength, "LDAP",
1480 ldapObject, "ActiveDirectory", activeDirectoryObject, "Oem",
1481 oemObject))
1482 {
1483 return;
1484 }
1485
1486 if (minPasswordLength)
1487 {
1488 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001489 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001490 if (ec)
1491 {
1492 messages::internalError(asyncResp->res);
1493 return;
1494 }
1495 messages::success(asyncResp->res);
1496 },
1497 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1498 "org.freedesktop.DBus.Properties", "Set",
1499 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
1500 dbus::utility::DbusVariantType(*minPasswordLength));
1501 }
1502
1503 if (maxPasswordLength)
1504 {
1505 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1506 }
1507
1508 if (ldapObject)
1509 {
1510 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1511 }
1512
1513 if (std::optional<nlohmann::json> oemOpenBMCObject;
1514 oemObject && json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1515 oemOpenBMCObject))
1516 {
1517 if (std::optional<nlohmann::json> authMethodsObject;
1518 oemOpenBMCObject &&
1519 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1520 "AuthMethods", authMethodsObject))
1521 {
1522 if (authMethodsObject)
1523 {
1524 handleAuthMethodsPatch(*authMethodsObject, asyncResp);
1525 }
1526 }
1527 }
1528
1529 if (activeDirectoryObject)
1530 {
1531 handleLDAPPatch(*activeDirectoryObject, asyncResp, "ActiveDirectory");
1532 }
1533
1534 if (unlockTimeout)
1535 {
1536 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001537 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001538 if (ec)
1539 {
1540 messages::internalError(asyncResp->res);
1541 return;
1542 }
1543 messages::success(asyncResp->res);
1544 },
1545 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1546 "org.freedesktop.DBus.Properties", "Set",
1547 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
1548 dbus::utility::DbusVariantType(*unlockTimeout));
1549 }
1550 if (lockoutThreshold)
1551 {
1552 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001553 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001554 if (ec)
1555 {
1556 messages::internalError(asyncResp->res);
1557 return;
1558 }
1559 messages::success(asyncResp->res);
1560 },
1561 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1562 "org.freedesktop.DBus.Properties", "Set",
1563 "xyz.openbmc_project.User.AccountPolicy",
1564 "MaxLoginAttemptBeforeLockout",
1565 dbus::utility::DbusVariantType(*lockoutThreshold));
1566 }
1567}
1568
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001569inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001570 App& app, const crow::Request& req,
1571 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1572{
1573 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1574 {
1575 return;
1576 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001577 asyncResp->res.addHeader(
1578 boost::beast::http::field::link,
1579 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1580}
1581
1582inline void handleAccountCollectionGet(
1583 App& app, const crow::Request& req,
1584 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1585{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001586 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1587 {
1588 return;
1589 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001590
1591 if (req.session == nullptr)
1592 {
1593 messages::internalError(asyncResp->res);
1594 return;
1595 }
1596
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001597 asyncResp->res.addHeader(
1598 boost::beast::http::field::link,
1599 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001600
1601 asyncResp->res.jsonValue["@odata.id"] =
1602 "/redfish/v1/AccountService/Accounts";
1603 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1604 "ManagerAccountCollection";
1605 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1606 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1607
1608 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001609 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001610
1611 std::string thisUser;
1612 if (req.session)
1613 {
1614 thisUser = req.session->username;
1615 }
1616 crow::connections::systemBus->async_method_call(
1617 [asyncResp, thisUser, effectiveUserPrivileges](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001618 const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001619 const dbus::utility::ManagedObjectType& users) {
1620 if (ec)
1621 {
1622 messages::internalError(asyncResp->res);
1623 return;
1624 }
1625
1626 bool userCanSeeAllAccounts =
1627 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1628
1629 bool userCanSeeSelf =
1630 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1631
1632 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1633 memberArray = nlohmann::json::array();
1634
1635 for (const auto& userpath : users)
1636 {
1637 std::string user = userpath.first.filename();
1638 if (user.empty())
1639 {
1640 messages::internalError(asyncResp->res);
1641 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1642
1643 return;
1644 }
1645
1646 // As clarified by Redfish here:
1647 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1648 // Users without ConfigureUsers, only see their own
1649 // account. Users with ConfigureUsers, see all
1650 // accounts.
1651 if (userCanSeeAllAccounts || (thisUser == user && userCanSeeSelf))
1652 {
1653 nlohmann::json::object_t member;
Patrick Williams89492a12023-05-10 07:51:34 -05001654 member["@odata.id"] = "/redfish/v1/AccountService/Accounts/" +
1655 user;
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001656 memberArray.emplace_back(std::move(member));
Ed Tanous1ef4c342022-05-12 16:12:36 -07001657 }
1658 }
1659 asyncResp->res.jsonValue["Members@odata.count"] = memberArray.size();
1660 },
1661 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1662 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1663}
1664
Ninad Palsule97e90da2023-05-17 14:04:52 -05001665inline void processAfterCreateUser(
1666 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1667 const std::string& username, const std::string& password,
1668 const boost::system::error_code& ec, sdbusplus::message_t& m)
1669{
1670 if (ec)
1671 {
1672 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1673 return;
1674 }
1675
1676 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1677 {
1678 // At this point we have a user that's been
1679 // created, but the password set
1680 // failed.Something is wrong, so delete the user
1681 // that we've already created
1682 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1683 tempObjPath /= username;
1684 const std::string userPath(tempObjPath);
1685
1686 crow::connections::systemBus->async_method_call(
1687 [asyncResp, password](const boost::system::error_code& ec3) {
1688 if (ec3)
1689 {
1690 messages::internalError(asyncResp->res);
1691 return;
1692 }
1693
1694 // If password is invalid
1695 messages::propertyValueFormatError(asyncResp->res, password,
1696 "Password");
1697 },
1698 "xyz.openbmc_project.User.Manager", userPath,
1699 "xyz.openbmc_project.Object.Delete", "Delete");
1700
1701 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1702 return;
1703 }
1704
1705 messages::created(asyncResp->res);
1706 asyncResp->res.addHeader("Location",
1707 "/redfish/v1/AccountService/Accounts/" + username);
1708}
1709
1710inline void processAfterGetAllGroups(
1711 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1712 const std::string& username, const std::string& password,
1713 const std::optional<std::string>& roleId, std::optional<bool> enabled,
1714 const std::vector<std::string>& allGroupsList)
1715
1716{
Ninad Palsule3e72c202023-03-27 17:19:55 -05001717 std::vector<std::string> userGroups;
1718 for (const auto& grp : allGroupsList)
1719 {
1720 // Console access is provided to the user who is a member of
1721 // hostconsole group and has a administrator role. So, set
1722 // hostconsole group only for the administrator.
1723 if ((grp != "hostconsole") || (roleId == "priv-admin"))
1724 {
1725 userGroups.emplace_back(grp);
1726 }
1727 }
1728
Ninad Palsule97e90da2023-05-17 14:04:52 -05001729 crow::connections::systemBus->async_method_call(
1730 [asyncResp, username, password](const boost::system::error_code& ec2,
1731 sdbusplus::message_t& m) {
1732 processAfterCreateUser(asyncResp, username, password, ec2, m);
1733 },
1734 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ninad Palsule3e72c202023-03-27 17:19:55 -05001735 "xyz.openbmc_project.User.Manager", "CreateUser", username, userGroups,
1736 *roleId, *enabled);
Ninad Palsule97e90da2023-05-17 14:04:52 -05001737}
1738
Ed Tanous1ef4c342022-05-12 16:12:36 -07001739inline void handleAccountCollectionPost(
1740 App& app, const crow::Request& req,
1741 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1742{
1743 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1744 {
1745 return;
1746 }
1747 std::string username;
1748 std::string password;
1749 std::optional<std::string> roleId("User");
1750 std::optional<bool> enabled = true;
1751 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", username,
1752 "Password", password, "RoleId", roleId,
1753 "Enabled", enabled))
1754 {
1755 return;
1756 }
1757
1758 std::string priv = getPrivilegeFromRoleId(*roleId);
1759 if (priv.empty())
1760 {
1761 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
1762 return;
1763 }
Asmitha Karunanithi239adf82022-03-25 02:59:03 -05001764 roleId = priv;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001765
1766 // Reading AllGroups property
1767 sdbusplus::asio::getProperty<std::vector<std::string>>(
1768 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1769 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager",
1770 "AllGroups",
1771 [asyncResp, username, password{std::move(password)}, roleId,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001772 enabled](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001773 const std::vector<std::string>& allGroupsList) {
1774 if (ec)
1775 {
1776 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1777 messages::internalError(asyncResp->res);
1778 return;
1779 }
1780
1781 if (allGroupsList.empty())
1782 {
1783 messages::internalError(asyncResp->res);
1784 return;
1785 }
1786
Ninad Palsule97e90da2023-05-17 14:04:52 -05001787 processAfterGetAllGroups(asyncResp, username, password, roleId, enabled,
1788 allGroupsList);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001789 });
1790}
1791
1792inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001793 handleAccountHead(App& app, const crow::Request& req,
1794 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1795 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001796{
1797 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1798 {
1799 return;
1800 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001801 asyncResp->res.addHeader(
1802 boost::beast::http::field::link,
1803 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1804}
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001805
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001806inline void
1807 handleAccountGet(App& app, const crow::Request& req,
1808 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1809 const std::string& accountName)
1810{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001811 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1812 {
1813 return;
1814 }
1815 asyncResp->res.addHeader(
1816 boost::beast::http::field::link,
1817 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1818
Ed Tanous1ef4c342022-05-12 16:12:36 -07001819#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1820 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001821 messages::resourceNotFound(asyncResp->res, "ManagerAccount", accountName);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001822 return;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001823#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001824
Ed Tanous1ef4c342022-05-12 16:12:36 -07001825 if (req.session == nullptr)
1826 {
1827 messages::internalError(asyncResp->res);
1828 return;
1829 }
1830 if (req.session->username != accountName)
1831 {
1832 // At this point we've determined that the user is trying to
1833 // modify a user that isn't them. We need to verify that they
1834 // have permissions to modify other users, so re-run the auth
1835 // check with the same permissions, minus ConfigureSelf.
1836 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001837 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001838 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
1839 "ConfigureManager"};
1840 if (!effectiveUserPrivileges.isSupersetOf(
1841 requiredPermissionsToChangeNonSelf))
1842 {
1843 BMCWEB_LOG_DEBUG << "GET Account denied access";
1844 messages::insufficientPrivilege(asyncResp->res);
1845 return;
1846 }
1847 }
1848
1849 crow::connections::systemBus->async_method_call(
1850 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001851 accountName](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001852 const dbus::utility::ManagedObjectType& users) {
1853 if (ec)
1854 {
1855 messages::internalError(asyncResp->res);
1856 return;
1857 }
1858 const auto userIt = std::find_if(
1859 users.begin(), users.end(),
1860 [accountName](
1861 const std::pair<sdbusplus::message::object_path,
1862 dbus::utility::DBusInteracesMap>& user) {
1863 return accountName == user.first.filename();
1864 });
1865
1866 if (userIt == users.end())
1867 {
1868 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1869 accountName);
1870 return;
1871 }
1872
1873 asyncResp->res.jsonValue["@odata.type"] =
1874 "#ManagerAccount.v1_4_0.ManagerAccount";
1875 asyncResp->res.jsonValue["Name"] = "User Account";
1876 asyncResp->res.jsonValue["Description"] = "User Account";
1877 asyncResp->res.jsonValue["Password"] = nullptr;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001878
1879 for (const auto& interface : userIt->second)
1880 {
1881 if (interface.first == "xyz.openbmc_project.User.Attributes")
1882 {
1883 for (const auto& property : interface.second)
1884 {
1885 if (property.first == "UserEnabled")
1886 {
1887 const bool* userEnabled =
1888 std::get_if<bool>(&property.second);
1889 if (userEnabled == nullptr)
1890 {
1891 BMCWEB_LOG_ERROR << "UserEnabled wasn't a bool";
1892 messages::internalError(asyncResp->res);
1893 return;
1894 }
1895 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
1896 }
1897 else if (property.first == "UserLockedForFailedAttempt")
1898 {
1899 const bool* userLocked =
1900 std::get_if<bool>(&property.second);
1901 if (userLocked == nullptr)
1902 {
1903 BMCWEB_LOG_ERROR << "UserLockedForF"
1904 "ailedAttempt "
1905 "wasn't a bool";
1906 messages::internalError(asyncResp->res);
1907 return;
1908 }
1909 asyncResp->res.jsonValue["Locked"] = *userLocked;
1910 asyncResp->res
1911 .jsonValue["Locked@Redfish.AllowableValues"] = {
1912 "false"}; // can only unlock accounts
1913 }
1914 else if (property.first == "UserPrivilege")
1915 {
1916 const std::string* userPrivPtr =
1917 std::get_if<std::string>(&property.second);
1918 if (userPrivPtr == nullptr)
1919 {
1920 BMCWEB_LOG_ERROR << "UserPrivilege wasn't a "
1921 "string";
1922 messages::internalError(asyncResp->res);
1923 return;
1924 }
1925 std::string role = getRoleIdFromPrivilege(*userPrivPtr);
1926 if (role.empty())
1927 {
1928 BMCWEB_LOG_ERROR << "Invalid user role";
1929 messages::internalError(asyncResp->res);
1930 return;
1931 }
1932 asyncResp->res.jsonValue["RoleId"] = role;
1933
1934 nlohmann::json& roleEntry =
1935 asyncResp->res.jsonValue["Links"]["Role"];
1936 roleEntry["@odata.id"] =
1937 "/redfish/v1/AccountService/Roles/" + role;
1938 }
1939 else if (property.first == "UserPasswordExpired")
1940 {
1941 const bool* userPasswordExpired =
1942 std::get_if<bool>(&property.second);
1943 if (userPasswordExpired == nullptr)
1944 {
1945 BMCWEB_LOG_ERROR
1946 << "UserPasswordExpired wasn't a bool";
1947 messages::internalError(asyncResp->res);
1948 return;
1949 }
1950 asyncResp->res.jsonValue["PasswordChangeRequired"] =
1951 *userPasswordExpired;
1952 }
Abhishek Patelc7229812022-02-01 10:07:15 -06001953 else if (property.first == "UserGroups")
1954 {
1955 const std::vector<std::string>* userGroups =
1956 std::get_if<std::vector<std::string>>(
1957 &property.second);
1958 if (userGroups == nullptr)
1959 {
1960 BMCWEB_LOG_ERROR
1961 << "userGroups wasn't a string vector";
1962 messages::internalError(asyncResp->res);
1963 return;
1964 }
1965 if (!translateUserGroup(*userGroups, asyncResp->res))
1966 {
1967 BMCWEB_LOG_ERROR << "userGroups mapping failed";
1968 messages::internalError(asyncResp->res);
1969 return;
1970 }
1971 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001972 }
1973 }
1974 }
1975
1976 asyncResp->res.jsonValue["@odata.id"] =
1977 "/redfish/v1/AccountService/Accounts/" + accountName;
1978 asyncResp->res.jsonValue["Id"] = accountName;
1979 asyncResp->res.jsonValue["UserName"] = accountName;
1980 },
1981 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1982 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1983}
1984
1985inline void
Gunnar Mills20fc3072023-01-27 15:13:36 -06001986 handleAccountDelete(App& app, const crow::Request& req,
1987 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1988 const std::string& username)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001989{
1990 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1991 {
1992 return;
1993 }
1994
1995#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1996 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001997 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001998 return;
1999
2000#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
2001 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
2002 tempObjPath /= username;
2003 const std::string userPath(tempObjPath);
2004
2005 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002006 [asyncResp, username](const boost::system::error_code& ec) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07002007 if (ec)
2008 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08002009 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
Ed Tanous1ef4c342022-05-12 16:12:36 -07002010 username);
2011 return;
2012 }
2013
2014 messages::accountRemoved(asyncResp->res);
2015 },
2016 "xyz.openbmc_project.User.Manager", userPath,
2017 "xyz.openbmc_project.Object.Delete", "Delete");
2018}
2019
2020inline void
2021 handleAccountPatch(App& app, const crow::Request& req,
2022 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2023 const std::string& username)
2024{
2025 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2026 {
2027 return;
2028 }
2029#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
2030 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08002031 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002032 return;
2033
2034#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
2035 std::optional<std::string> newUserName;
2036 std::optional<std::string> password;
2037 std::optional<bool> enabled;
2038 std::optional<std::string> roleId;
2039 std::optional<bool> locked;
2040
2041 if (req.session == nullptr)
2042 {
2043 messages::internalError(asyncResp->res);
2044 return;
2045 }
2046
2047 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002048 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002049 Privileges configureUsers = {"ConfigureUsers"};
2050 bool userHasConfigureUsers =
2051 effectiveUserPrivileges.isSupersetOf(configureUsers);
2052 if (userHasConfigureUsers)
2053 {
2054 // Users with ConfigureUsers can modify for all users
2055 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
2056 newUserName, "Password", password,
2057 "RoleId", roleId, "Enabled", enabled,
2058 "Locked", locked))
2059 {
2060 return;
2061 }
2062 }
2063 else
2064 {
2065 // ConfigureSelf accounts can only modify their own account
2066 if (username != req.session->username)
2067 {
2068 messages::insufficientPrivilege(asyncResp->res);
2069 return;
2070 }
2071
2072 // ConfigureSelf accounts can only modify their password
2073 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
2074 password))
2075 {
2076 return;
2077 }
2078 }
2079
2080 // if user name is not provided in the patch method or if it
2081 // matches the user name in the URI, then we are treating it as
2082 // updating user properties other then username. If username
2083 // provided doesn't match the URI, then we are treating this as
2084 // user rename request.
2085 if (!newUserName || (newUserName.value() == username))
2086 {
2087 updateUserProperties(asyncResp, username, password, enabled, roleId,
2088 locked);
2089 return;
2090 }
2091 crow::connections::systemBus->async_method_call(
2092 [asyncResp, username, password(std::move(password)),
2093 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002094 locked](const boost::system::error_code& ec, sdbusplus::message_t& m) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07002095 if (ec)
2096 {
2097 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
2098 username);
2099 return;
2100 }
2101
2102 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
2103 locked);
2104 },
2105 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
2106 "xyz.openbmc_project.User.Manager", "RenameUser", username,
2107 *newUserName);
2108}
2109
Ed Tanous6c51eab2021-06-03 12:30:29 -07002110inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07002111{
Ed Tanous6c51eab2021-06-03 12:30:29 -07002112 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002113 .privileges(redfish::privileges::headAccountService)
2114 .methods(boost::beast::http::verb::head)(
2115 std::bind_front(handleAccountServiceHead, std::ref(app)));
2116
2117 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07002118 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002119 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002120 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002121
Ed Tanousf5ffd802021-07-19 10:55:33 -07002122 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06002123 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07002124 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002125 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07002126
Ed Tanous6c51eab2021-06-03 12:30:29 -07002127 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002128 .privileges(redfish::privileges::headManagerAccountCollection)
2129 .methods(boost::beast::http::verb::head)(
2130 std::bind_front(handleAccountCollectionHead, std::ref(app)));
2131
2132 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002133 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002134 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002135 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07002136
Ed Tanous6c51eab2021-06-03 12:30:29 -07002137 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002138 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002139 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002140 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07002141
2142 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002143 .privileges(redfish::privileges::headManagerAccount)
2144 .methods(boost::beast::http::verb::head)(
2145 std::bind_front(handleAccountHead, std::ref(app)));
2146
2147 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07002148 .privileges(redfish::privileges::getManagerAccount)
2149 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002150 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002151
2152 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002153 // TODO this privilege should be using the generated endpoints, but
2154 // because of the special handling of ConfigureSelf, it's not able to
2155 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07002156 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
2157 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002158 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002159
2160 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002161 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002162 .methods(boost::beast::http::verb::delete_)(
Gunnar Mills20fc3072023-01-27 15:13:36 -06002163 std::bind_front(handleAccountDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002164}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002165
Ed Tanous1abe55e2018-09-05 08:30:59 -07002166} // namespace redfish