blob: 91bd024952bc6f5b8ef060bd96d6b5105f2ed64e [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
John Edward Broadbent7e860f12021-04-08 15:57:16 -070018#include <app.hpp>
Ratan Gupta24c85422019-01-30 19:41:24 +053019#include <dbus_utility.hpp>
Ed Tanous65b0dc32018-09-19 16:04:03 -070020#include <error_messages.hpp>
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070021#include <openbmc_dbus_rest.hpp>
Ed Tanous52cc1122020-07-18 13:51:21 -070022#include <persistent_data.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -070023#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070024#include <registries/privilege_registry.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070025#include <sdbusplus/asio/property.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020026#include <sdbusplus/unpack_properties.hpp>
27#include <utils/dbus_utils.hpp>
Ed Tanousa8408792018-09-05 16:08:38 -070028#include <utils/json_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050029
Ed Tanous1abe55e2018-09-05 08:30:59 -070030namespace redfish
31{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010032
Ed Tanous23a21a12020-07-25 04:45:05 +000033constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053034 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070035constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053036 "/xyz/openbmc_project/user/ldap/active_directory";
37
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053038constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053039constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
40constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
41constexpr const char* ldapConfigInterface =
42 "xyz.openbmc_project.User.Ldap.Config";
43constexpr const char* ldapCreateInterface =
44 "xyz.openbmc_project.User.Ldap.Create";
45constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053046constexpr const char* ldapPrivMapperInterface =
47 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053048constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
49constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
50constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
51constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
52constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
53
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060054struct LDAPRoleMapData
55{
56 std::string groupName;
57 std::string privilege;
58};
59
Ratan Gupta6973a582018-12-13 18:25:44 +053060struct LDAPConfigData
61{
62 std::string uri{};
63 std::string bindDN{};
64 std::string baseDN{};
65 std::string searchScope{};
66 std::string serverType{};
67 bool serviceEnabled = false;
68 std::string userNameAttribute{};
69 std::string groupAttribute{};
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060070 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053071};
72
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060073inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053074{
75 if (role == "priv-admin")
76 {
77 return "Administrator";
78 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070079 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053080 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053081 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053082 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070083 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053084 {
85 return "Operator";
86 }
Ed Tanous26f69762022-01-25 09:49:11 -080087 if (role.empty() || (role == "priv-noaccess"))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +000088 {
89 return "NoAccess";
90 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +053091 return "";
92}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060093inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053094{
95 if (role == "Administrator")
96 {
97 return "priv-admin";
98 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070099 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530100 {
101 return "priv-user";
102 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700103 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530104 {
105 return "priv-operator";
106 }
Ed Tanous26f69762022-01-25 09:49:11 -0800107 if ((role == "NoAccess") || (role.empty()))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +0000108 {
109 return "priv-noaccess";
110 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530111 return "";
112}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700113
zhanghch058d1b46d2021-04-01 11:18:24 +0800114inline void userErrorMessageHandler(
115 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
116 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000117{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000118 if (e == nullptr)
119 {
120 messages::internalError(asyncResp->res);
121 return;
122 }
123
Manojkiran Eda055806b2020-11-03 09:36:28 +0530124 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000125 if (strcmp(errorMessage,
126 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
127 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800128 messages::resourceAlreadyExists(asyncResp->res, "ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000129 "UserName", newUser);
130 }
131 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
132 "UserNameDoesNotExist") == 0)
133 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800134 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000135 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700136 else if ((strcmp(errorMessage,
137 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
138 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800139 (strcmp(
140 errorMessage,
141 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
142 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000143 {
144 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
145 }
146 else if (strcmp(errorMessage,
147 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
148 {
149 messages::createLimitReachedForResource(asyncResp->res);
150 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000151 else
152 {
153 messages::internalError(asyncResp->res);
154 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000155}
156
Ed Tanous81ce6092020-12-17 16:54:55 +0000157inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000158 const LDAPConfigData& confData,
159 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530160{
Ratan Guptaab828d72019-04-22 14:18:33 +0530161 std::string service =
162 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600163
Ed Tanous14766872022-03-15 10:44:42 -0700164 nlohmann::json& ldap = jsonResponse[ldapType];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600165
Ed Tanous14766872022-03-15 10:44:42 -0700166 ldap["ServiceEnabled"] = confData.serviceEnabled;
167 ldap["ServiceAddresses"] = nlohmann::json::array({confData.uri});
168 ldap["Authentication"]["AuthenticationType"] = "UsernameAndPassword";
169 ldap["Authentication"]["Username"] = confData.bindDN;
170 ldap["Authentication"]["Password"] = nullptr;
171
172 ldap["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"] =
173 nlohmann::json::array({confData.baseDN});
174 ldap["LDAPService"]["SearchSettings"]["UsernameAttribute"] =
175 confData.userNameAttribute;
176 ldap["LDAPService"]["SearchSettings"]["GroupsAttribute"] =
177 confData.groupAttribute;
178
179 nlohmann::json& roleMapArray = ldap["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600180 roleMapArray = nlohmann::json::array();
Ed Tanous9eb808c2022-01-25 10:19:23 -0800181 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600182 {
183 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
184 << obj.second.groupName << "\n";
Ed Tanous613dabe2022-07-09 11:17:36 -0700185
186 nlohmann::json::array_t remoteGroupArray;
187 nlohmann::json::object_t remoteGroup;
188 remoteGroup["RemoteGroup"] = obj.second.groupName;
189 remoteGroupArray.emplace_back(std::move(remoteGroup));
190 roleMapArray.emplace_back(std::move(remoteGroupArray));
191
192 nlohmann::json::array_t localRoleArray;
193 nlohmann::json::object_t localRole;
194 localRole["LocalRole"] = getRoleIdFromPrivilege(obj.second.privilege);
195 localRoleArray.emplace_back(std::move(localRole));
196 roleMapArray.emplace_back(std::move(localRoleArray));
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600197 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530198}
199
200/**
Ratan Gupta06785242019-07-26 22:30:16 +0530201 * @brief validates given JSON input and then calls appropriate method to
202 * create, to delete or to set Rolemapping object based on the given input.
203 *
204 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000205inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800206 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530207 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700208 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530209{
210 for (size_t index = 0; index < input.size(); index++)
211 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700212 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530213
214 if (thisJson.is_null())
215 {
216 // delete the existing object
217 if (index < roleMapObjData.size())
218 {
219 crow::connections::systemBus->async_method_call(
220 [asyncResp, roleMapObjData, serverType,
221 index](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700222 if (ec)
223 {
224 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
225 messages::internalError(asyncResp->res);
226 return;
227 }
228 asyncResp->res
229 .jsonValue[serverType]["RemoteRoleMapping"][index] =
230 nullptr;
Ratan Gupta06785242019-07-26 22:30:16 +0530231 },
232 ldapDbusService, roleMapObjData[index].first,
233 "xyz.openbmc_project.Object.Delete", "Delete");
234 }
235 else
236 {
237 BMCWEB_LOG_ERROR << "Can't delete the object";
238 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800239 asyncResp->res,
240 thisJson.dump(2, ' ', true,
241 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530242 "RemoteRoleMapping/" + std::to_string(index));
243 return;
244 }
245 }
246 else if (thisJson.empty())
247 {
248 // Don't do anything for the empty objects,parse next json
249 // eg {"RemoteRoleMapping",[{}]}
250 }
251 else
252 {
253 // update/create the object
254 std::optional<std::string> remoteGroup;
255 std::optional<std::string> localRole;
256
Ed Tanousf23b7292020-10-15 09:41:17 -0700257 // This is a copy, but it's required in this case because of how
258 // readJson is structured
259 nlohmann::json thisJsonCopy = thisJson;
260 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
261 "RemoteGroup", remoteGroup, "LocalRole",
262 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530263 {
264 continue;
265 }
266
267 // Update existing RoleMapping Object
268 if (index < roleMapObjData.size())
269 {
270 BMCWEB_LOG_DEBUG << "Update Role Map Object";
271 // If "RemoteGroup" info is provided
272 if (remoteGroup)
273 {
274 crow::connections::systemBus->async_method_call(
275 [asyncResp, roleMapObjData, serverType, index,
276 remoteGroup](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700277 if (ec)
278 {
279 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
280 messages::internalError(asyncResp->res);
281 return;
282 }
283 asyncResp->res
284 .jsonValue[serverType]["RemoteRoleMapping"][index]
285 ["RemoteGroup"] = *remoteGroup;
Ratan Gupta06785242019-07-26 22:30:16 +0530286 },
287 ldapDbusService, roleMapObjData[index].first,
288 propertyInterface, "Set",
289 "xyz.openbmc_project.User.PrivilegeMapperEntry",
290 "GroupName",
Ed Tanous168e20c2021-12-13 14:39:53 -0800291 dbus::utility::DbusVariantType(
292 std::move(*remoteGroup)));
Ratan Gupta06785242019-07-26 22:30:16 +0530293 }
294
295 // If "LocalRole" info is provided
296 if (localRole)
297 {
298 crow::connections::systemBus->async_method_call(
299 [asyncResp, roleMapObjData, serverType, index,
300 localRole](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700301 if (ec)
302 {
303 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
304 messages::internalError(asyncResp->res);
305 return;
306 }
307 asyncResp->res
308 .jsonValue[serverType]["RemoteRoleMapping"][index]
309 ["LocalRole"] = *localRole;
Ratan Gupta06785242019-07-26 22:30:16 +0530310 },
311 ldapDbusService, roleMapObjData[index].first,
312 propertyInterface, "Set",
313 "xyz.openbmc_project.User.PrivilegeMapperEntry",
314 "Privilege",
Ed Tanous168e20c2021-12-13 14:39:53 -0800315 dbus::utility::DbusVariantType(
Ratan Gupta06785242019-07-26 22:30:16 +0530316 getPrivilegeFromRoleId(std::move(*localRole))));
317 }
318 }
319 // Create a new RoleMapping Object.
320 else
321 {
322 BMCWEB_LOG_DEBUG
323 << "setRoleMappingProperties: Creating new Object";
324 std::string pathString =
325 "RemoteRoleMapping/" + std::to_string(index);
326
327 if (!localRole)
328 {
329 messages::propertyMissing(asyncResp->res,
330 pathString + "/LocalRole");
331 continue;
332 }
333 if (!remoteGroup)
334 {
335 messages::propertyMissing(asyncResp->res,
336 pathString + "/RemoteGroup");
337 continue;
338 }
339
340 std::string dbusObjectPath;
341 if (serverType == "ActiveDirectory")
342 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700343 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530344 }
345 else if (serverType == "LDAP")
346 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000347 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530348 }
349
350 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
351 << ",LocalRole=" << *localRole;
352
353 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700354 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530355 remoteGroup](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700356 if (ec)
357 {
358 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
359 messages::internalError(asyncResp->res);
360 return;
361 }
362 nlohmann::json& remoteRoleJson =
363 asyncResp->res
364 .jsonValue[serverType]["RemoteRoleMapping"];
365 nlohmann::json::object_t roleMapEntry;
366 roleMapEntry["LocalRole"] = *localRole;
367 roleMapEntry["RemoteGroup"] = *remoteGroup;
368 remoteRoleJson.push_back(std::move(roleMapEntry));
Ratan Gupta06785242019-07-26 22:30:16 +0530369 },
370 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700371 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530372 getPrivilegeFromRoleId(std::move(*localRole)));
373 }
374 }
375 }
376}
377
378/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530379 * Function that retrieves all properties for LDAP config object
380 * into JSON
381 */
382template <typename CallbackFunc>
383inline void getLDAPConfigData(const std::string& ldapType,
384 CallbackFunc&& callback)
385{
Ratan Guptaab828d72019-04-22 14:18:33 +0530386
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600387 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530388 ldapConfigInterface};
389
390 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600391 [callback, ldapType](const boost::system::error_code ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800392 const dbus::utility::MapperGetObject& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700393 if (ec || resp.empty())
394 {
395 BMCWEB_LOG_ERROR
396 << "DBUS response error during getting of service name: " << ec;
397 LDAPConfigData empty{};
398 callback(false, empty, ldapType);
399 return;
400 }
401 std::string service = resp.begin()->first;
402 crow::connections::systemBus->async_method_call(
403 [callback,
404 ldapType](const boost::system::error_code errorCode,
405 const dbus::utility::ManagedObjectType& ldapObjects) {
406 LDAPConfigData confData{};
407 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600408 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700409 callback(false, confData, ldapType);
410 BMCWEB_LOG_ERROR << "D-Bus responses error: " << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600411 return;
412 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600413
Ed Tanous002d39b2022-05-31 08:59:27 -0700414 std::string ldapDbusType;
415 std::string searchString;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600416
Ed Tanous002d39b2022-05-31 08:59:27 -0700417 if (ldapType == "LDAP")
418 {
419 ldapDbusType =
420 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
421 searchString = "openldap";
422 }
423 else if (ldapType == "ActiveDirectory")
424 {
425 ldapDbusType =
426 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
427 searchString = "active_directory";
428 }
429 else
430 {
431 BMCWEB_LOG_ERROR << "Can't get the DbusType for the given type="
432 << ldapType;
433 callback(false, confData, ldapType);
434 return;
435 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600436
Ed Tanous002d39b2022-05-31 08:59:27 -0700437 std::string ldapEnableInterfaceStr = ldapEnableInterface;
438 std::string ldapConfigInterfaceStr = ldapConfigInterface;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600439
Ed Tanous002d39b2022-05-31 08:59:27 -0700440 for (const auto& object : ldapObjects)
441 {
442 // let's find the object whose ldap type is equal to the
443 // given type
444 if (object.first.str.find(searchString) == std::string::npos)
445 {
446 continue;
447 }
448
449 for (const auto& interface : object.second)
450 {
451 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600452 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700453 // rest of the properties are string.
454 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600455 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700456 if (property.first == "Enabled")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600457 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700458 const bool* value =
459 std::get_if<bool>(&property.second);
460 if (value == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600461 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700462 continue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600463 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700464 confData.serviceEnabled = *value;
465 break;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600466 }
467 }
468 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700469 else if (interface.first == ldapConfigInterfaceStr)
470 {
471
472 for (const auto& property : interface.second)
473 {
474 const std::string* strValue =
475 std::get_if<std::string>(&property.second);
476 if (strValue == nullptr)
477 {
478 continue;
479 }
480 if (property.first == "LDAPServerURI")
481 {
482 confData.uri = *strValue;
483 }
484 else if (property.first == "LDAPBindDN")
485 {
486 confData.bindDN = *strValue;
487 }
488 else if (property.first == "LDAPBaseDN")
489 {
490 confData.baseDN = *strValue;
491 }
492 else if (property.first == "LDAPSearchScope")
493 {
494 confData.searchScope = *strValue;
495 }
496 else if (property.first == "GroupNameAttribute")
497 {
498 confData.groupAttribute = *strValue;
499 }
500 else if (property.first == "UserNameAttribute")
501 {
502 confData.userNameAttribute = *strValue;
503 }
504 else if (property.first == "LDAPType")
505 {
506 confData.serverType = *strValue;
507 }
508 }
509 }
510 else if (interface.first ==
511 "xyz.openbmc_project.User.PrivilegeMapperEntry")
512 {
513 LDAPRoleMapData roleMapData{};
514 for (const auto& property : interface.second)
515 {
516 const std::string* strValue =
517 std::get_if<std::string>(&property.second);
518
519 if (strValue == nullptr)
520 {
521 continue;
522 }
523
524 if (property.first == "GroupName")
525 {
526 roleMapData.groupName = *strValue;
527 }
528 else if (property.first == "Privilege")
529 {
530 roleMapData.privilege = *strValue;
531 }
532 }
533
534 confData.groupRoleList.emplace_back(object.first.str,
535 roleMapData);
536 }
537 }
538 }
539 callback(true, confData, ldapType);
540 },
541 service, ldapRootObject, dbusObjManagerIntf, "GetManagedObjects");
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600542 },
543 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
Ed Tanous23a21a12020-07-25 04:45:05 +0000544 ldapConfigObjectName, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530545}
546
Ed Tanous6c51eab2021-06-03 12:30:29 -0700547/**
548 * @brief parses the authentication section under the LDAP
549 * @param input JSON data
550 * @param asyncResp pointer to the JSON response
551 * @param userName userName to be filled from the given JSON.
552 * @param password password to be filled from the given JSON.
553 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700554inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700555 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
556 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700557{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700558 std::optional<std::string> authType;
559
560 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
561 authType, "Username", username, "Password",
562 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700563 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700564 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700565 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700566 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530567 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700568 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530569 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700570 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530571 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700572 messages::propertyValueNotInList(asyncResp->res, *authType,
573 "AuthenticationType");
574 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530575 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700576}
577/**
578 * @brief parses the LDAPService section under the LDAP
579 * @param input JSON data
580 * @param asyncResp pointer to the JSON response
581 * @param baseDNList baseDN to be filled from the given JSON.
582 * @param userNameAttribute userName to be filled from the given JSON.
583 * @param groupaAttribute password to be filled from the given JSON.
584 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530585
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700586inline void
587 parseLDAPServiceJson(nlohmann::json input,
588 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
589 std::optional<std::vector<std::string>>& baseDNList,
590 std::optional<std::string>& userNameAttribute,
591 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700592{
593 std::optional<nlohmann::json> searchSettings;
594
595 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
596 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530597 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700598 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530599 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700600 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530601 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700602 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530603 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700604 if (!json_util::readJson(*searchSettings, asyncResp->res,
605 "BaseDistinguishedNames", baseDNList,
606 "UsernameAttribute", userNameAttribute,
607 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530608 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700609 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530610 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700611}
612/**
613 * @brief updates the LDAP server address and updates the
614 json response with the new value.
615 * @param serviceAddressList address to be updated.
616 * @param asyncResp pointer to the JSON response
617 * @param ldapServerElementName Type of LDAP
618 server(openLDAP/ActiveDirectory)
619 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530620
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700621inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700622 const std::vector<std::string>& serviceAddressList,
623 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
624 const std::string& ldapServerElementName,
625 const std::string& ldapConfigObject)
626{
627 crow::connections::systemBus->async_method_call(
628 [asyncResp, ldapServerElementName,
629 serviceAddressList](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700630 if (ec)
631 {
632 BMCWEB_LOG_DEBUG
633 << "Error Occurred in updating the service address";
634 messages::internalError(asyncResp->res);
635 return;
636 }
637 std::vector<std::string> modifiedserviceAddressList = {
638 serviceAddressList.front()};
639 asyncResp->res.jsonValue[ldapServerElementName]["ServiceAddresses"] =
640 modifiedserviceAddressList;
641 if ((serviceAddressList).size() > 1)
642 {
643 messages::propertyValueModified(asyncResp->res, "ServiceAddresses",
644 serviceAddressList.front());
645 }
646 BMCWEB_LOG_DEBUG << "Updated the service address";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700647 },
648 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
649 ldapConfigInterface, "LDAPServerURI",
Ed Tanous168e20c2021-12-13 14:39:53 -0800650 dbus::utility::DbusVariantType(serviceAddressList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700651}
652/**
653 * @brief updates the LDAP Bind DN and updates the
654 json response with the new value.
655 * @param username name of the user which needs to be updated.
656 * @param asyncResp pointer to the JSON response
657 * @param ldapServerElementName Type of LDAP
658 server(openLDAP/ActiveDirectory)
659 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530660
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700661inline void
662 handleUserNamePatch(const std::string& username,
663 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
664 const std::string& ldapServerElementName,
665 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700666{
667 crow::connections::systemBus->async_method_call(
668 [asyncResp, username,
669 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700670 if (ec)
671 {
672 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
673 messages::internalError(asyncResp->res);
674 return;
675 }
676 asyncResp->res
677 .jsonValue[ldapServerElementName]["Authentication"]["Username"] =
678 username;
679 BMCWEB_LOG_DEBUG << "Updated the username";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700680 },
681 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800682 ldapConfigInterface, "LDAPBindDN",
683 dbus::utility::DbusVariantType(username));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700684}
685
686/**
687 * @brief updates the LDAP password
688 * @param password : ldap password which needs to be updated.
689 * @param asyncResp pointer to the JSON response
690 * @param ldapServerElementName Type of LDAP
691 * server(openLDAP/ActiveDirectory)
692 */
693
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700694inline void
695 handlePasswordPatch(const std::string& password,
696 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
697 const std::string& ldapServerElementName,
698 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700699{
700 crow::connections::systemBus->async_method_call(
701 [asyncResp, password,
702 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700703 if (ec)
704 {
705 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
706 messages::internalError(asyncResp->res);
707 return;
708 }
709 asyncResp->res
710 .jsonValue[ldapServerElementName]["Authentication"]["Password"] =
711 "";
712 BMCWEB_LOG_DEBUG << "Updated the password";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700713 },
714 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
715 ldapConfigInterface, "LDAPBindDNPassword",
Ed Tanous168e20c2021-12-13 14:39:53 -0800716 dbus::utility::DbusVariantType(password));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700717}
718
719/**
720 * @brief updates the LDAP BaseDN and updates the
721 json response with the new value.
722 * @param baseDNList baseDN list which needs to be updated.
723 * @param asyncResp pointer to the JSON response
724 * @param ldapServerElementName Type of LDAP
725 server(openLDAP/ActiveDirectory)
726 */
727
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700728inline void
729 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
730 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
731 const std::string& ldapServerElementName,
732 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700733{
734 crow::connections::systemBus->async_method_call(
735 [asyncResp, baseDNList,
736 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700737 if (ec)
738 {
739 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
740 messages::internalError(asyncResp->res);
741 return;
742 }
743 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
744 auto& searchSettingsJson =
745 serverTypeJson["LDAPService"]["SearchSettings"];
746 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
747 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
748 if (baseDNList.size() > 1)
749 {
750 messages::propertyValueModified(
751 asyncResp->res, "BaseDistinguishedNames", baseDNList.front());
752 }
753 BMCWEB_LOG_DEBUG << "Updated the base DN";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700754 },
755 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
756 ldapConfigInterface, "LDAPBaseDN",
Ed Tanous168e20c2021-12-13 14:39:53 -0800757 dbus::utility::DbusVariantType(baseDNList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700758}
759/**
760 * @brief updates the LDAP user name attribute and updates the
761 json response with the new value.
762 * @param userNameAttribute attribute to be updated.
763 * @param asyncResp pointer to the JSON response
764 * @param ldapServerElementName Type of LDAP
765 server(openLDAP/ActiveDirectory)
766 */
767
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700768inline void
769 handleUserNameAttrPatch(const std::string& userNameAttribute,
770 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
771 const std::string& ldapServerElementName,
772 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700773{
774 crow::connections::systemBus->async_method_call(
775 [asyncResp, userNameAttribute,
776 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700777 if (ec)
778 {
779 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
780 "username attribute";
781 messages::internalError(asyncResp->res);
782 return;
783 }
784 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
785 auto& searchSettingsJson =
786 serverTypeJson["LDAPService"]["SearchSettings"];
787 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
788 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700789 },
790 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
791 ldapConfigInterface, "UserNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800792 dbus::utility::DbusVariantType(userNameAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700793}
794/**
795 * @brief updates the LDAP group attribute and updates the
796 json response with the new value.
797 * @param groupsAttribute attribute to be updated.
798 * @param asyncResp pointer to the JSON response
799 * @param ldapServerElementName Type of LDAP
800 server(openLDAP/ActiveDirectory)
801 */
802
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700803inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700804 const std::string& groupsAttribute,
805 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
806 const std::string& ldapServerElementName,
807 const std::string& ldapConfigObject)
808{
809 crow::connections::systemBus->async_method_call(
810 [asyncResp, groupsAttribute,
811 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700812 if (ec)
813 {
814 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
815 "groupname attribute";
816 messages::internalError(asyncResp->res);
817 return;
818 }
819 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
820 auto& searchSettingsJson =
821 serverTypeJson["LDAPService"]["SearchSettings"];
822 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
823 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700824 },
825 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
826 ldapConfigInterface, "GroupNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800827 dbus::utility::DbusVariantType(groupsAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700828}
829/**
830 * @brief updates the LDAP service enable and updates the
831 json response with the new value.
832 * @param input JSON data.
833 * @param asyncResp pointer to the JSON response
834 * @param ldapServerElementName Type of LDAP
835 server(openLDAP/ActiveDirectory)
836 */
837
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700838inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700839 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
840 const std::string& ldapServerElementName,
841 const std::string& ldapConfigObject)
842{
843 crow::connections::systemBus->async_method_call(
844 [asyncResp, serviceEnabled,
845 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700846 if (ec)
847 {
848 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the service enable";
849 messages::internalError(asyncResp->res);
850 return;
851 }
852 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
853 serviceEnabled;
854 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700855 },
856 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800857 ldapEnableInterface, "Enabled",
858 dbus::utility::DbusVariantType(serviceEnabled));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700859}
860
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700861inline void
862 handleAuthMethodsPatch(nlohmann::json& input,
863 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700864{
865 std::optional<bool> basicAuth;
866 std::optional<bool> cookie;
867 std::optional<bool> sessionToken;
868 std::optional<bool> xToken;
869 std::optional<bool> tls;
870
871 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
872 "Cookie", cookie, "SessionToken", sessionToken,
873 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530874 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700875 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
876 return;
877 }
878
879 // Make a copy of methods configuration
880 persistent_data::AuthConfigMethods authMethodsConfig =
881 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
882
883 if (basicAuth)
884 {
885#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
886 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800887 asyncResp->res,
888 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700889 return;
890#endif
891 authMethodsConfig.basic = *basicAuth;
892 }
893
894 if (cookie)
895 {
896#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800897 messages::actionNotSupported(
898 asyncResp->res,
899 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700900 return;
901#endif
902 authMethodsConfig.cookie = *cookie;
903 }
904
905 if (sessionToken)
906 {
907#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
908 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800909 asyncResp->res,
910 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700911 return;
912#endif
913 authMethodsConfig.sessionToken = *sessionToken;
914 }
915
916 if (xToken)
917 {
918#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800919 messages::actionNotSupported(
920 asyncResp->res,
921 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700922 return;
923#endif
924 authMethodsConfig.xtoken = *xToken;
925 }
926
927 if (tls)
928 {
929#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800930 messages::actionNotSupported(
931 asyncResp->res,
932 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700933 return;
934#endif
935 authMethodsConfig.tls = *tls;
936 }
937
938 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
939 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
940 !authMethodsConfig.tls)
941 {
942 // Do not allow user to disable everything
943 messages::actionNotSupported(asyncResp->res,
944 "of disabling all available methods");
945 return;
946 }
947
948 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
949 authMethodsConfig);
950 // Save configuration immediately
951 persistent_data::getConfig().writeData();
952
953 messages::success(asyncResp->res);
954}
955
956/**
957 * @brief Get the required values from the given JSON, validates the
958 * value and create the LDAP config object.
959 * @param input JSON data
960 * @param asyncResp pointer to the JSON response
961 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
962 */
963
964inline void handleLDAPPatch(nlohmann::json& input,
965 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
966 const std::string& serverType)
967{
968 std::string dbusObjectPath;
969 if (serverType == "ActiveDirectory")
970 {
971 dbusObjectPath = adConfigObject;
972 }
973 else if (serverType == "LDAP")
974 {
975 dbusObjectPath = ldapConfigObjectName;
976 }
977 else
978 {
979 return;
980 }
981
982 std::optional<nlohmann::json> authentication;
983 std::optional<nlohmann::json> ldapService;
984 std::optional<std::vector<std::string>> serviceAddressList;
985 std::optional<bool> serviceEnabled;
986 std::optional<std::vector<std::string>> baseDNList;
987 std::optional<std::string> userNameAttribute;
988 std::optional<std::string> groupsAttribute;
989 std::optional<std::string> userName;
990 std::optional<std::string> password;
991 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
992
993 if (!json_util::readJson(input, asyncResp->res, "Authentication",
994 authentication, "LDAPService", ldapService,
995 "ServiceAddresses", serviceAddressList,
996 "ServiceEnabled", serviceEnabled,
997 "RemoteRoleMapping", remoteRoleMapData))
998 {
999 return;
1000 }
1001
1002 if (authentication)
1003 {
1004 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1005 password);
1006 }
1007 if (ldapService)
1008 {
1009 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1010 userNameAttribute, groupsAttribute);
1011 }
1012 if (serviceAddressList)
1013 {
Ed Tanous26f69762022-01-25 09:49:11 -08001014 if (serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301015 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001016 messages::propertyValueNotInList(asyncResp->res, "[]",
1017 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001018 return;
1019 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001020 }
1021 if (baseDNList)
1022 {
Ed Tanous26f69762022-01-25 09:49:11 -08001023 if (baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +05301024 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001025 messages::propertyValueNotInList(asyncResp->res, "[]",
1026 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301027 return;
1028 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001029 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301030
Ed Tanous6c51eab2021-06-03 12:30:29 -07001031 // nothing to update, then return
1032 if (!userName && !password && !serviceAddressList && !baseDNList &&
1033 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1034 !remoteRoleMapData)
1035 {
1036 return;
1037 }
1038
1039 // Get the existing resource first then keep modifying
1040 // whenever any property gets updated.
Ed Tanous002d39b2022-05-31 08:59:27 -07001041 getLDAPConfigData(
1042 serverType,
1043 [asyncResp, userName, password, baseDNList, userNameAttribute,
1044 groupsAttribute, serviceAddressList, serviceEnabled, dbusObjectPath,
1045 remoteRoleMapData](bool success, const LDAPConfigData& confData,
1046 const std::string& serverT) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001047 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301048 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001049 messages::internalError(asyncResp->res);
1050 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301051 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001052 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1053 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301054 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001055 // Disable the service first and update the rest of
1056 // the properties.
1057 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301058 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001059
Ratan Gupta8a07d282019-03-16 08:33:47 +05301060 if (serviceAddressList)
1061 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001062 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1063 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301064 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001065 if (userName)
1066 {
1067 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1068 }
1069 if (password)
1070 {
1071 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1072 }
1073
Ratan Gupta8a07d282019-03-16 08:33:47 +05301074 if (baseDNList)
1075 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001076 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1077 }
1078 if (userNameAttribute)
1079 {
1080 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1081 dbusObjectPath);
1082 }
1083 if (groupsAttribute)
1084 {
1085 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1086 dbusObjectPath);
1087 }
1088 if (serviceEnabled)
1089 {
1090 // if user has given the value as true then enable
1091 // the service. if user has given false then no-op
1092 // as service is already stopped.
1093 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301094 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001095 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1096 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301097 }
1098 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001099 else
1100 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001101 // if user has not given the service enabled value
1102 // then revert it to the same state as it was
1103 // before.
1104 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1105 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001106 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001107
Ed Tanous6c51eab2021-06-03 12:30:29 -07001108 if (remoteRoleMapData)
1109 {
1110 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1111 *remoteRoleMapData);
1112 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001113 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001114}
1115
1116inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1117 const std::string& username,
1118 std::optional<std::string> password,
1119 std::optional<bool> enabled,
1120 std::optional<std::string> roleId,
1121 std::optional<bool> locked)
1122{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301123 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1124 tempObjPath /= username;
1125 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001126
1127 dbus::utility::checkDbusPathExists(
1128 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001129 [dbusObjectPath, username, password(std::move(password)),
1130 roleId(std::move(roleId)), enabled, locked,
1131 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001132 if (rc <= 0)
1133 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001134 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
Ed Tanous002d39b2022-05-31 08:59:27 -07001135 username);
1136 return;
1137 }
1138
1139 if (password)
1140 {
1141 int retval = pamUpdatePassword(username, *password);
1142
1143 if (retval == PAM_USER_UNKNOWN)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001144 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001145 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1146 username);
Ed Tanous002d39b2022-05-31 08:59:27 -07001147 }
1148 else if (retval == PAM_AUTHTOK_ERR)
1149 {
1150 // If password is invalid
1151 messages::propertyValueFormatError(asyncResp->res, *password,
1152 "Password");
1153 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1154 }
1155 else if (retval != PAM_SUCCESS)
1156 {
1157 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001158 return;
1159 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001160 else
Ed Tanous6c51eab2021-06-03 12:30:29 -07001161 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001162 messages::success(asyncResp->res);
1163 }
1164 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001165
Ed Tanous002d39b2022-05-31 08:59:27 -07001166 if (enabled)
1167 {
1168 crow::connections::systemBus->async_method_call(
1169 [asyncResp](const boost::system::error_code ec) {
1170 if (ec)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001171 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001172 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001173 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001174 return;
1175 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001176 messages::success(asyncResp->res);
1177 return;
1178 },
1179 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1180 "org.freedesktop.DBus.Properties", "Set",
1181 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1182 dbus::utility::DbusVariantType{*enabled});
1183 }
1184
1185 if (roleId)
1186 {
1187 std::string priv = getPrivilegeFromRoleId(*roleId);
1188 if (priv.empty())
1189 {
1190 messages::propertyValueNotInList(asyncResp->res, *roleId,
1191 "RoleId");
1192 return;
1193 }
1194 if (priv == "priv-noaccess")
1195 {
1196 priv = "";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001197 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001198
Ed Tanous002d39b2022-05-31 08:59:27 -07001199 crow::connections::systemBus->async_method_call(
1200 [asyncResp](const boost::system::error_code ec) {
1201 if (ec)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001202 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001203 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1204 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001205 return;
1206 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001207 messages::success(asyncResp->res);
1208 },
1209 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1210 "org.freedesktop.DBus.Properties", "Set",
1211 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1212 dbus::utility::DbusVariantType{priv});
1213 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001214
Ed Tanous002d39b2022-05-31 08:59:27 -07001215 if (locked)
1216 {
1217 // admin can unlock the account which is locked by
1218 // successive authentication failures but admin should
1219 // not be allowed to lock an account.
1220 if (*locked)
1221 {
1222 messages::propertyValueNotInList(asyncResp->res, "true",
1223 "Locked");
1224 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001225 }
1226
Ed Tanous002d39b2022-05-31 08:59:27 -07001227 crow::connections::systemBus->async_method_call(
1228 [asyncResp](const boost::system::error_code ec) {
1229 if (ec)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001230 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001231 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1232 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001233 return;
1234 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001235 messages::success(asyncResp->res);
1236 return;
1237 },
1238 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1239 "org.freedesktop.DBus.Properties", "Set",
1240 "xyz.openbmc_project.User.Attributes",
1241 "UserLockedForFailedAttempt",
1242 dbus::utility::DbusVariantType{*locked});
1243 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001244 });
1245}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001246
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001247inline void handleAccountServiceHead(
1248 App& app, const crow::Request& req,
1249 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001250{
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001251
Ed Tanous1ef4c342022-05-12 16:12:36 -07001252 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1253 {
1254 return;
1255 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001256 asyncResp->res.addHeader(
1257 boost::beast::http::field::link,
1258 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1259}
1260
1261inline void
1262 handleAccountServiceGet(App& app, const crow::Request& req,
1263 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1264{
1265 handleAccountServiceHead(app, req, asyncResp);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001266 const persistent_data::AuthConfigMethods& authMethodsConfig =
1267 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1268
1269 nlohmann::json& json = asyncResp->res.jsonValue;
1270 json["@odata.id"] = "/redfish/v1/AccountService";
1271 json["@odata.type"] = "#AccountService."
1272 "v1_10_0.AccountService";
1273 json["Id"] = "AccountService";
1274 json["Name"] = "Account Service";
1275 json["Description"] = "Account Service";
1276 json["ServiceEnabled"] = true;
1277 json["MaxPasswordLength"] = 20;
1278 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1279 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
1280 json["Oem"]["OpenBMC"]["@odata.type"] =
1281 "#OemAccountService.v1_0_0.AccountService";
1282 json["Oem"]["OpenBMC"]["@odata.id"] =
1283 "/redfish/v1/AccountService#/Oem/OpenBMC";
1284 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1285 authMethodsConfig.basic;
1286 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1287 authMethodsConfig.sessionToken;
1288 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1289 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1290 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1291
1292 // /redfish/v1/AccountService/LDAP/Certificates is something only
1293 // ConfigureManager can access then only display when the user has
1294 // permissions ConfigureManager
1295 Privileges effectiveUserPrivileges =
1296 redfish::getUserPrivileges(req.userRole);
1297
1298 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1299 effectiveUserPrivileges))
1300 {
1301 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1302 "/redfish/v1/AccountService/LDAP/Certificates";
1303 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001304 sdbusplus::asio::getAllProperties(
1305 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1306 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.AccountPolicy",
Ed Tanous1ef4c342022-05-12 16:12:36 -07001307 [asyncResp](const boost::system::error_code ec,
1308 const dbus::utility::DBusPropertiesMap& propertiesList) {
1309 if (ec)
1310 {
1311 messages::internalError(asyncResp->res);
1312 return;
1313 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001314
Ed Tanous1ef4c342022-05-12 16:12:36 -07001315 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1316 << "properties for AccountService";
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001317
1318 const uint8_t* minPasswordLength = nullptr;
1319 const uint32_t* accountUnlockTimeout = nullptr;
1320 const uint16_t* maxLoginAttemptBeforeLockout = nullptr;
1321
1322 const bool success = sdbusplus::unpackPropertiesNoThrow(
1323 dbus_utils::UnpackErrorPrinter(), propertiesList,
1324 "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout",
1325 accountUnlockTimeout, "MaxLoginAttemptBeforeLockout",
1326 maxLoginAttemptBeforeLockout);
1327
1328 if (!success)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001329 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001330 messages::internalError(asyncResp->res);
1331 return;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001332 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001333
1334 if (minPasswordLength != nullptr)
1335 {
1336 asyncResp->res.jsonValue["MinPasswordLength"] = *minPasswordLength;
1337 }
1338
1339 if (accountUnlockTimeout != nullptr)
1340 {
1341 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1342 *accountUnlockTimeout;
1343 }
1344
1345 if (maxLoginAttemptBeforeLockout != nullptr)
1346 {
1347 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1348 *maxLoginAttemptBeforeLockout;
1349 }
1350 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001351
Ed Tanous02cad962022-06-30 16:50:15 -07001352 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001353 const std::string& ldapType) {
1354 if (!success)
1355 {
1356 return;
1357 }
1358 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1359 };
1360
1361 getLDAPConfigData("LDAP", callback);
1362 getLDAPConfigData("ActiveDirectory", callback);
1363}
1364
1365inline void handleAccountServicePatch(
1366 App& app, const crow::Request& req,
1367 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1368{
1369 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1370 {
1371 return;
1372 }
1373 std::optional<uint32_t> unlockTimeout;
1374 std::optional<uint16_t> lockoutThreshold;
1375 std::optional<uint8_t> minPasswordLength;
1376 std::optional<uint16_t> maxPasswordLength;
1377 std::optional<nlohmann::json> ldapObject;
1378 std::optional<nlohmann::json> activeDirectoryObject;
1379 std::optional<nlohmann::json> oemObject;
1380
1381 if (!json_util::readJsonPatch(
1382 req, asyncResp->res, "AccountLockoutDuration", unlockTimeout,
1383 "AccountLockoutThreshold", lockoutThreshold, "MaxPasswordLength",
1384 maxPasswordLength, "MinPasswordLength", minPasswordLength, "LDAP",
1385 ldapObject, "ActiveDirectory", activeDirectoryObject, "Oem",
1386 oemObject))
1387 {
1388 return;
1389 }
1390
1391 if (minPasswordLength)
1392 {
1393 crow::connections::systemBus->async_method_call(
1394 [asyncResp](const boost::system::error_code ec) {
1395 if (ec)
1396 {
1397 messages::internalError(asyncResp->res);
1398 return;
1399 }
1400 messages::success(asyncResp->res);
1401 },
1402 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1403 "org.freedesktop.DBus.Properties", "Set",
1404 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
1405 dbus::utility::DbusVariantType(*minPasswordLength));
1406 }
1407
1408 if (maxPasswordLength)
1409 {
1410 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1411 }
1412
1413 if (ldapObject)
1414 {
1415 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1416 }
1417
1418 if (std::optional<nlohmann::json> oemOpenBMCObject;
1419 oemObject && json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1420 oemOpenBMCObject))
1421 {
1422 if (std::optional<nlohmann::json> authMethodsObject;
1423 oemOpenBMCObject &&
1424 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1425 "AuthMethods", authMethodsObject))
1426 {
1427 if (authMethodsObject)
1428 {
1429 handleAuthMethodsPatch(*authMethodsObject, asyncResp);
1430 }
1431 }
1432 }
1433
1434 if (activeDirectoryObject)
1435 {
1436 handleLDAPPatch(*activeDirectoryObject, asyncResp, "ActiveDirectory");
1437 }
1438
1439 if (unlockTimeout)
1440 {
1441 crow::connections::systemBus->async_method_call(
1442 [asyncResp](const boost::system::error_code ec) {
1443 if (ec)
1444 {
1445 messages::internalError(asyncResp->res);
1446 return;
1447 }
1448 messages::success(asyncResp->res);
1449 },
1450 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1451 "org.freedesktop.DBus.Properties", "Set",
1452 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
1453 dbus::utility::DbusVariantType(*unlockTimeout));
1454 }
1455 if (lockoutThreshold)
1456 {
1457 crow::connections::systemBus->async_method_call(
1458 [asyncResp](const boost::system::error_code ec) {
1459 if (ec)
1460 {
1461 messages::internalError(asyncResp->res);
1462 return;
1463 }
1464 messages::success(asyncResp->res);
1465 },
1466 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1467 "org.freedesktop.DBus.Properties", "Set",
1468 "xyz.openbmc_project.User.AccountPolicy",
1469 "MaxLoginAttemptBeforeLockout",
1470 dbus::utility::DbusVariantType(*lockoutThreshold));
1471 }
1472}
1473
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001474inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001475 App& app, const crow::Request& req,
1476 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1477{
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001478
Ed Tanous1ef4c342022-05-12 16:12:36 -07001479 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1480 {
1481 return;
1482 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001483 asyncResp->res.addHeader(
1484 boost::beast::http::field::link,
1485 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1486}
1487
1488inline void handleAccountCollectionGet(
1489 App& app, const crow::Request& req,
1490 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1491{
1492 handleAccountCollectionHead(app, req, asyncResp);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001493
1494 asyncResp->res.jsonValue["@odata.id"] =
1495 "/redfish/v1/AccountService/Accounts";
1496 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1497 "ManagerAccountCollection";
1498 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1499 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1500
1501 Privileges effectiveUserPrivileges =
1502 redfish::getUserPrivileges(req.userRole);
1503
1504 std::string thisUser;
1505 if (req.session)
1506 {
1507 thisUser = req.session->username;
1508 }
1509 crow::connections::systemBus->async_method_call(
1510 [asyncResp, thisUser, effectiveUserPrivileges](
1511 const boost::system::error_code ec,
1512 const dbus::utility::ManagedObjectType& users) {
1513 if (ec)
1514 {
1515 messages::internalError(asyncResp->res);
1516 return;
1517 }
1518
1519 bool userCanSeeAllAccounts =
1520 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1521
1522 bool userCanSeeSelf =
1523 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1524
1525 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1526 memberArray = nlohmann::json::array();
1527
1528 for (const auto& userpath : users)
1529 {
1530 std::string user = userpath.first.filename();
1531 if (user.empty())
1532 {
1533 messages::internalError(asyncResp->res);
1534 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1535
1536 return;
1537 }
1538
1539 // As clarified by Redfish here:
1540 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1541 // Users without ConfigureUsers, only see their own
1542 // account. Users with ConfigureUsers, see all
1543 // accounts.
1544 if (userCanSeeAllAccounts || (thisUser == user && userCanSeeSelf))
1545 {
1546 nlohmann::json::object_t member;
1547 member["@odata.id"] =
1548 "/redfish/v1/AccountService/Accounts/" + user;
1549 memberArray.push_back(std::move(member));
1550 }
1551 }
1552 asyncResp->res.jsonValue["Members@odata.count"] = memberArray.size();
1553 },
1554 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1555 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1556}
1557
1558inline void handleAccountCollectionPost(
1559 App& app, const crow::Request& req,
1560 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1561{
1562 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1563 {
1564 return;
1565 }
1566 std::string username;
1567 std::string password;
1568 std::optional<std::string> roleId("User");
1569 std::optional<bool> enabled = true;
1570 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", username,
1571 "Password", password, "RoleId", roleId,
1572 "Enabled", enabled))
1573 {
1574 return;
1575 }
1576
1577 std::string priv = getPrivilegeFromRoleId(*roleId);
1578 if (priv.empty())
1579 {
1580 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
1581 return;
1582 }
1583 // TODO: Following override will be reverted once support in
1584 // phosphor-user-manager is added. In order to avoid dependency
1585 // issues, this is added in bmcweb, which will removed, once
1586 // phosphor-user-manager supports priv-noaccess.
1587 if (priv == "priv-noaccess")
1588 {
1589 roleId = "";
1590 }
1591 else
1592 {
1593 roleId = priv;
1594 }
1595
1596 // Reading AllGroups property
1597 sdbusplus::asio::getProperty<std::vector<std::string>>(
1598 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1599 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager",
1600 "AllGroups",
1601 [asyncResp, username, password{std::move(password)}, roleId,
1602 enabled](const boost::system::error_code ec,
1603 const std::vector<std::string>& allGroupsList) {
1604 if (ec)
1605 {
1606 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1607 messages::internalError(asyncResp->res);
1608 return;
1609 }
1610
1611 if (allGroupsList.empty())
1612 {
1613 messages::internalError(asyncResp->res);
1614 return;
1615 }
1616
1617 crow::connections::systemBus->async_method_call(
1618 [asyncResp, username, password](const boost::system::error_code ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -05001619 sdbusplus::message_t& m) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001620 if (ec2)
1621 {
1622 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1623 return;
1624 }
1625
1626 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1627 {
1628 // At this point we have a user that's been
1629 // created, but the password set
1630 // failed.Something is wrong, so delete the user
1631 // that we've already created
1632 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1633 tempObjPath /= username;
1634 const std::string userPath(tempObjPath);
1635
1636 crow::connections::systemBus->async_method_call(
1637 [asyncResp, password](const boost::system::error_code ec3) {
1638 if (ec3)
1639 {
1640 messages::internalError(asyncResp->res);
1641 return;
1642 }
1643
1644 // If password is invalid
1645 messages::propertyValueFormatError(asyncResp->res, password,
1646 "Password");
1647 },
1648 "xyz.openbmc_project.User.Manager", userPath,
1649 "xyz.openbmc_project.Object.Delete", "Delete");
1650
1651 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1652 return;
1653 }
1654
1655 messages::created(asyncResp->res);
1656 asyncResp->res.addHeader(
1657 "Location", "/redfish/v1/AccountService/Accounts/" + username);
1658 },
1659 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1660 "xyz.openbmc_project.User.Manager", "CreateUser", username,
1661 allGroupsList, *roleId, *enabled);
1662 });
1663}
1664
1665inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001666 handleAccountHead(App& app, const crow::Request& req,
1667 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1668 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001669{
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001670
Ed Tanous1ef4c342022-05-12 16:12:36 -07001671 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1672 {
1673 return;
1674 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001675 asyncResp->res.addHeader(
1676 boost::beast::http::field::link,
1677 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1678}
1679inline void
1680 handleAccountGet(App& app, const crow::Request& req,
1681 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1682 const std::string& accountName)
1683{
1684 handleAccountHead(app, req, asyncResp, accountName);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001685#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1686 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001687 messages::resourceNotFound(asyncResp->res, "ManagerAccount", accountName);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001688 return;
1689
1690#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1691 if (req.session == nullptr)
1692 {
1693 messages::internalError(asyncResp->res);
1694 return;
1695 }
1696 if (req.session->username != accountName)
1697 {
1698 // At this point we've determined that the user is trying to
1699 // modify a user that isn't them. We need to verify that they
1700 // have permissions to modify other users, so re-run the auth
1701 // check with the same permissions, minus ConfigureSelf.
1702 Privileges effectiveUserPrivileges =
1703 redfish::getUserPrivileges(req.userRole);
1704 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
1705 "ConfigureManager"};
1706 if (!effectiveUserPrivileges.isSupersetOf(
1707 requiredPermissionsToChangeNonSelf))
1708 {
1709 BMCWEB_LOG_DEBUG << "GET Account denied access";
1710 messages::insufficientPrivilege(asyncResp->res);
1711 return;
1712 }
1713 }
1714
1715 crow::connections::systemBus->async_method_call(
1716 [asyncResp,
1717 accountName](const boost::system::error_code ec,
1718 const dbus::utility::ManagedObjectType& users) {
1719 if (ec)
1720 {
1721 messages::internalError(asyncResp->res);
1722 return;
1723 }
1724 const auto userIt = std::find_if(
1725 users.begin(), users.end(),
1726 [accountName](
1727 const std::pair<sdbusplus::message::object_path,
1728 dbus::utility::DBusInteracesMap>& user) {
1729 return accountName == user.first.filename();
1730 });
1731
1732 if (userIt == users.end())
1733 {
1734 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1735 accountName);
1736 return;
1737 }
1738
1739 asyncResp->res.jsonValue["@odata.type"] =
1740 "#ManagerAccount.v1_4_0.ManagerAccount";
1741 asyncResp->res.jsonValue["Name"] = "User Account";
1742 asyncResp->res.jsonValue["Description"] = "User Account";
1743 asyncResp->res.jsonValue["Password"] = nullptr;
Ed Tanous613dabe2022-07-09 11:17:36 -07001744 asyncResp->res.jsonValue["AccountTypes"] =
1745 nlohmann::json::array_t({"Redfish"});
Ed Tanous1ef4c342022-05-12 16:12:36 -07001746
1747 for (const auto& interface : userIt->second)
1748 {
1749 if (interface.first == "xyz.openbmc_project.User.Attributes")
1750 {
1751 for (const auto& property : interface.second)
1752 {
1753 if (property.first == "UserEnabled")
1754 {
1755 const bool* userEnabled =
1756 std::get_if<bool>(&property.second);
1757 if (userEnabled == nullptr)
1758 {
1759 BMCWEB_LOG_ERROR << "UserEnabled wasn't a bool";
1760 messages::internalError(asyncResp->res);
1761 return;
1762 }
1763 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
1764 }
1765 else if (property.first == "UserLockedForFailedAttempt")
1766 {
1767 const bool* userLocked =
1768 std::get_if<bool>(&property.second);
1769 if (userLocked == nullptr)
1770 {
1771 BMCWEB_LOG_ERROR << "UserLockedForF"
1772 "ailedAttempt "
1773 "wasn't a bool";
1774 messages::internalError(asyncResp->res);
1775 return;
1776 }
1777 asyncResp->res.jsonValue["Locked"] = *userLocked;
1778 asyncResp->res
1779 .jsonValue["Locked@Redfish.AllowableValues"] = {
1780 "false"}; // can only unlock accounts
1781 }
1782 else if (property.first == "UserPrivilege")
1783 {
1784 const std::string* userPrivPtr =
1785 std::get_if<std::string>(&property.second);
1786 if (userPrivPtr == nullptr)
1787 {
1788 BMCWEB_LOG_ERROR << "UserPrivilege wasn't a "
1789 "string";
1790 messages::internalError(asyncResp->res);
1791 return;
1792 }
1793 std::string role = getRoleIdFromPrivilege(*userPrivPtr);
1794 if (role.empty())
1795 {
1796 BMCWEB_LOG_ERROR << "Invalid user role";
1797 messages::internalError(asyncResp->res);
1798 return;
1799 }
1800 asyncResp->res.jsonValue["RoleId"] = role;
1801
1802 nlohmann::json& roleEntry =
1803 asyncResp->res.jsonValue["Links"]["Role"];
1804 roleEntry["@odata.id"] =
1805 "/redfish/v1/AccountService/Roles/" + role;
1806 }
1807 else if (property.first == "UserPasswordExpired")
1808 {
1809 const bool* userPasswordExpired =
1810 std::get_if<bool>(&property.second);
1811 if (userPasswordExpired == nullptr)
1812 {
1813 BMCWEB_LOG_ERROR
1814 << "UserPasswordExpired wasn't a bool";
1815 messages::internalError(asyncResp->res);
1816 return;
1817 }
1818 asyncResp->res.jsonValue["PasswordChangeRequired"] =
1819 *userPasswordExpired;
1820 }
1821 }
1822 }
1823 }
1824
1825 asyncResp->res.jsonValue["@odata.id"] =
1826 "/redfish/v1/AccountService/Accounts/" + accountName;
1827 asyncResp->res.jsonValue["Id"] = accountName;
1828 asyncResp->res.jsonValue["UserName"] = accountName;
1829 },
1830 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1831 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1832}
1833
1834inline void
1835 handleAccounttDelete(App& app, const crow::Request& req,
1836 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1837 const std::string& username)
1838{
1839 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1840 {
1841 return;
1842 }
1843
1844#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1845 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001846 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001847 return;
1848
1849#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1850 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1851 tempObjPath /= username;
1852 const std::string userPath(tempObjPath);
1853
1854 crow::connections::systemBus->async_method_call(
1855 [asyncResp, username](const boost::system::error_code ec) {
1856 if (ec)
1857 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001858 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
Ed Tanous1ef4c342022-05-12 16:12:36 -07001859 username);
1860 return;
1861 }
1862
1863 messages::accountRemoved(asyncResp->res);
1864 },
1865 "xyz.openbmc_project.User.Manager", userPath,
1866 "xyz.openbmc_project.Object.Delete", "Delete");
1867}
1868
1869inline void
1870 handleAccountPatch(App& app, const crow::Request& req,
1871 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1872 const std::string& username)
1873{
1874 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1875 {
1876 return;
1877 }
1878#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1879 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001880 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001881 return;
1882
1883#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1884 std::optional<std::string> newUserName;
1885 std::optional<std::string> password;
1886 std::optional<bool> enabled;
1887 std::optional<std::string> roleId;
1888 std::optional<bool> locked;
1889
1890 if (req.session == nullptr)
1891 {
1892 messages::internalError(asyncResp->res);
1893 return;
1894 }
1895
1896 Privileges effectiveUserPrivileges =
1897 redfish::getUserPrivileges(req.userRole);
1898 Privileges configureUsers = {"ConfigureUsers"};
1899 bool userHasConfigureUsers =
1900 effectiveUserPrivileges.isSupersetOf(configureUsers);
1901 if (userHasConfigureUsers)
1902 {
1903 // Users with ConfigureUsers can modify for all users
1904 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
1905 newUserName, "Password", password,
1906 "RoleId", roleId, "Enabled", enabled,
1907 "Locked", locked))
1908 {
1909 return;
1910 }
1911 }
1912 else
1913 {
1914 // ConfigureSelf accounts can only modify their own account
1915 if (username != req.session->username)
1916 {
1917 messages::insufficientPrivilege(asyncResp->res);
1918 return;
1919 }
1920
1921 // ConfigureSelf accounts can only modify their password
1922 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
1923 password))
1924 {
1925 return;
1926 }
1927 }
1928
1929 // if user name is not provided in the patch method or if it
1930 // matches the user name in the URI, then we are treating it as
1931 // updating user properties other then username. If username
1932 // provided doesn't match the URI, then we are treating this as
1933 // user rename request.
1934 if (!newUserName || (newUserName.value() == username))
1935 {
1936 updateUserProperties(asyncResp, username, password, enabled, roleId,
1937 locked);
1938 return;
1939 }
1940 crow::connections::systemBus->async_method_call(
1941 [asyncResp, username, password(std::move(password)),
1942 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Patrick Williams59d494e2022-07-22 19:26:55 -05001943 locked](const boost::system::error_code ec, sdbusplus::message_t& m) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001944 if (ec)
1945 {
1946 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
1947 username);
1948 return;
1949 }
1950
1951 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
1952 locked);
1953 },
1954 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1955 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1956 *newUserName);
1957}
1958
Ed Tanous6c51eab2021-06-03 12:30:29 -07001959inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001960{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001961
Ed Tanous6c51eab2021-06-03 12:30:29 -07001962 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001963 .privileges(redfish::privileges::headAccountService)
1964 .methods(boost::beast::http::verb::head)(
1965 std::bind_front(handleAccountServiceHead, std::ref(app)));
1966
1967 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001968 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07001969 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001970 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001971
Ed Tanousf5ffd802021-07-19 10:55:33 -07001972 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06001973 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07001974 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001975 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001976
Ed Tanous6c51eab2021-06-03 12:30:29 -07001977 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001978 .privileges(redfish::privileges::headManagerAccountCollection)
1979 .methods(boost::beast::http::verb::head)(
1980 std::bind_front(handleAccountCollectionHead, std::ref(app)));
1981
1982 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001983 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001984 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001985 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07001986
Ed Tanous6c51eab2021-06-03 12:30:29 -07001987 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001988 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001989 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001990 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07001991
1992 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001993 .privileges(redfish::privileges::headManagerAccount)
1994 .methods(boost::beast::http::verb::head)(
1995 std::bind_front(handleAccountHead, std::ref(app)));
1996
1997 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07001998 .privileges(redfish::privileges::getManagerAccount)
1999 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002000 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002001
2002 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002003 // TODO this privilege should be using the generated endpoints, but
2004 // because of the special handling of ConfigureSelf, it's not able to
2005 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07002006 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
2007 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002008 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002009
2010 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002011 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002012 .methods(boost::beast::http::verb::delete_)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002013 std::bind_front(handleAccounttDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002014}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002015
Ed Tanous1abe55e2018-09-05 08:30:59 -07002016} // namespace redfish