blob: 87707825ea5a6e39576be3c14768f9e8b435d580 [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 Tanoused398212021-06-09 17:05:54 -070023#include <registries/privilege_registry.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070024#include <sdbusplus/asio/property.hpp>
Ed Tanousa8408792018-09-05 16:08:38 -070025#include <utils/json_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace redfish
28{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010029
Ed Tanous23a21a12020-07-25 04:45:05 +000030constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053031 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070032constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053033 "/xyz/openbmc_project/user/ldap/active_directory";
34
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053035constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053036constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
37constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
38constexpr const char* ldapConfigInterface =
39 "xyz.openbmc_project.User.Ldap.Config";
40constexpr const char* ldapCreateInterface =
41 "xyz.openbmc_project.User.Ldap.Create";
42constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053043constexpr const char* ldapPrivMapperInterface =
44 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053045constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
46constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
47constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
48constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
49constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
50
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060051struct LDAPRoleMapData
52{
53 std::string groupName;
54 std::string privilege;
55};
56
Ratan Gupta6973a582018-12-13 18:25:44 +053057struct LDAPConfigData
58{
59 std::string uri{};
60 std::string bindDN{};
61 std::string baseDN{};
62 std::string searchScope{};
63 std::string serverType{};
64 bool serviceEnabled = false;
65 std::string userNameAttribute{};
66 std::string groupAttribute{};
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060067 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053068};
69
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020070using DbusInterfaceType = boost::container::flat_map<
Ed Tanous168e20c2021-12-13 14:39:53 -080071 std::string,
72 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>>;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020073
Ratan Gupta6973a582018-12-13 18:25:44 +053074using GetObjectType =
75 std::vector<std::pair<std::string, std::vector<std::string>>>;
AppaRao Puli84e12cb2018-10-11 01:28:15 +053076
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060077inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053078{
79 if (role == "priv-admin")
80 {
81 return "Administrator";
82 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070083 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053084 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053085 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053086 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070087 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053088 {
89 return "Operator";
90 }
Ed Tanous26f69762022-01-25 09:49:11 -080091 if (role.empty() || (role == "priv-noaccess"))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +000092 {
93 return "NoAccess";
94 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +053095 return "";
96}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060097inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053098{
99 if (role == "Administrator")
100 {
101 return "priv-admin";
102 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700103 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530104 {
105 return "priv-user";
106 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700107 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530108 {
109 return "priv-operator";
110 }
Ed Tanous26f69762022-01-25 09:49:11 -0800111 if ((role == "NoAccess") || (role.empty()))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +0000112 {
113 return "priv-noaccess";
114 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530115 return "";
116}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700117
zhanghch058d1b46d2021-04-01 11:18:24 +0800118inline void userErrorMessageHandler(
119 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
120 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000121{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000122 if (e == nullptr)
123 {
124 messages::internalError(asyncResp->res);
125 return;
126 }
127
Manojkiran Eda055806b2020-11-03 09:36:28 +0530128 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000129 if (strcmp(errorMessage,
130 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
131 {
132 messages::resourceAlreadyExists(asyncResp->res,
Gunnar Mills8114bd42020-06-11 20:55:21 -0500133 "#ManagerAccount.v1_4_0.ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000134 "UserName", newUser);
135 }
136 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
137 "UserNameDoesNotExist") == 0)
138 {
139 messages::resourceNotFound(
Gunnar Mills8114bd42020-06-11 20:55:21 -0500140 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000141 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700142 else if ((strcmp(errorMessage,
143 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
144 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800145 (strcmp(
146 errorMessage,
147 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
148 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000149 {
150 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
151 }
152 else if (strcmp(errorMessage,
153 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
154 {
155 messages::createLimitReachedForResource(asyncResp->res);
156 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000157 else
158 {
159 messages::internalError(asyncResp->res);
160 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000161}
162
Ed Tanous81ce6092020-12-17 16:54:55 +0000163inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000164 const LDAPConfigData& confData,
165 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530166{
Ratan Guptaab828d72019-04-22 14:18:33 +0530167 std::string service =
168 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600169 nlohmann::json ldap = {
Ratan Gupta6973a582018-12-13 18:25:44 +0530170 {"ServiceEnabled", confData.serviceEnabled},
171 {"ServiceAddresses", nlohmann::json::array({confData.uri})},
172 {"Authentication",
173 {{"AuthenticationType", "UsernameAndPassword"},
Ratan Gupta6973a582018-12-13 18:25:44 +0530174 {"Username", confData.bindDN},
175 {"Password", nullptr}}},
176 {"LDAPService",
177 {{"SearchSettings",
178 {{"BaseDistinguishedNames",
179 nlohmann::json::array({confData.baseDN})},
180 {"UsernameAttribute", confData.userNameAttribute},
181 {"GroupsAttribute", confData.groupAttribute}}}}},
182 };
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600183
Ed Tanous81ce6092020-12-17 16:54:55 +0000184 jsonResponse[ldapType].update(ldap);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600185
Ed Tanous81ce6092020-12-17 16:54:55 +0000186 nlohmann::json& roleMapArray = jsonResponse[ldapType]["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600187 roleMapArray = nlohmann::json::array();
Ed Tanous9eb808c2022-01-25 10:19:23 -0800188 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600189 {
190 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
191 << obj.second.groupName << "\n";
192 roleMapArray.push_back(
193 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
194 nlohmann::json::array(
195 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
196 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530197}
198
199/**
Ratan Gupta06785242019-07-26 22:30:16 +0530200 * @brief validates given JSON input and then calls appropriate method to
201 * create, to delete or to set Rolemapping object based on the given input.
202 *
203 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000204inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800205 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530206 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700207 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530208{
209 for (size_t index = 0; index < input.size(); index++)
210 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700211 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530212
213 if (thisJson.is_null())
214 {
215 // delete the existing object
216 if (index < roleMapObjData.size())
217 {
218 crow::connections::systemBus->async_method_call(
219 [asyncResp, roleMapObjData, serverType,
220 index](const boost::system::error_code ec) {
221 if (ec)
222 {
223 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
224 messages::internalError(asyncResp->res);
225 return;
226 }
227 asyncResp->res
228 .jsonValue[serverType]["RemoteRoleMapping"][index] =
229 nullptr;
230 },
231 ldapDbusService, roleMapObjData[index].first,
232 "xyz.openbmc_project.Object.Delete", "Delete");
233 }
234 else
235 {
236 BMCWEB_LOG_ERROR << "Can't delete the object";
237 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800238 asyncResp->res,
239 thisJson.dump(2, ' ', true,
240 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530241 "RemoteRoleMapping/" + std::to_string(index));
242 return;
243 }
244 }
245 else if (thisJson.empty())
246 {
247 // Don't do anything for the empty objects,parse next json
248 // eg {"RemoteRoleMapping",[{}]}
249 }
250 else
251 {
252 // update/create the object
253 std::optional<std::string> remoteGroup;
254 std::optional<std::string> localRole;
255
Ed Tanousf23b7292020-10-15 09:41:17 -0700256 // This is a copy, but it's required in this case because of how
257 // readJson is structured
258 nlohmann::json thisJsonCopy = thisJson;
259 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
260 "RemoteGroup", remoteGroup, "LocalRole",
261 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530262 {
263 continue;
264 }
265
266 // Update existing RoleMapping Object
267 if (index < roleMapObjData.size())
268 {
269 BMCWEB_LOG_DEBUG << "Update Role Map Object";
270 // If "RemoteGroup" info is provided
271 if (remoteGroup)
272 {
273 crow::connections::systemBus->async_method_call(
274 [asyncResp, roleMapObjData, serverType, index,
275 remoteGroup](const boost::system::error_code ec) {
276 if (ec)
277 {
278 BMCWEB_LOG_ERROR << "DBUS response error: "
279 << ec;
280 messages::internalError(asyncResp->res);
281 return;
282 }
283 asyncResp->res
284 .jsonValue[serverType]["RemoteRoleMapping"]
285 [index]["RemoteGroup"] = *remoteGroup;
286 },
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) {
301 if (ec)
302 {
303 BMCWEB_LOG_ERROR << "DBUS response error: "
304 << ec;
305 messages::internalError(asyncResp->res);
306 return;
307 }
308 asyncResp->res
309 .jsonValue[serverType]["RemoteRoleMapping"]
310 [index]["LocalRole"] = *localRole;
311 },
312 ldapDbusService, roleMapObjData[index].first,
313 propertyInterface, "Set",
314 "xyz.openbmc_project.User.PrivilegeMapperEntry",
315 "Privilege",
Ed Tanous168e20c2021-12-13 14:39:53 -0800316 dbus::utility::DbusVariantType(
Ratan Gupta06785242019-07-26 22:30:16 +0530317 getPrivilegeFromRoleId(std::move(*localRole))));
318 }
319 }
320 // Create a new RoleMapping Object.
321 else
322 {
323 BMCWEB_LOG_DEBUG
324 << "setRoleMappingProperties: Creating new Object";
325 std::string pathString =
326 "RemoteRoleMapping/" + std::to_string(index);
327
328 if (!localRole)
329 {
330 messages::propertyMissing(asyncResp->res,
331 pathString + "/LocalRole");
332 continue;
333 }
334 if (!remoteGroup)
335 {
336 messages::propertyMissing(asyncResp->res,
337 pathString + "/RemoteGroup");
338 continue;
339 }
340
341 std::string dbusObjectPath;
342 if (serverType == "ActiveDirectory")
343 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700344 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530345 }
346 else if (serverType == "LDAP")
347 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000348 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530349 }
350
351 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
352 << ",LocalRole=" << *localRole;
353
354 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700355 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530356 remoteGroup](const boost::system::error_code ec) {
357 if (ec)
358 {
359 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
360 messages::internalError(asyncResp->res);
361 return;
362 }
363 nlohmann::json& remoteRoleJson =
364 asyncResp->res
365 .jsonValue[serverType]["RemoteRoleMapping"];
366 remoteRoleJson.push_back(
367 {{"LocalRole", *localRole},
368 {"RemoteGroup", *remoteGroup}});
369 },
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,
392 const GetObjectType& resp) {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600393 if (ec || resp.empty())
394 {
George Liu0fda0f12021-11-16 10:06:17 +0800395 BMCWEB_LOG_ERROR
396 << "DBUS response error during getting of service name: "
397 << ec;
Ed Tanous23a21a12020-07-25 04:45:05 +0000398 LDAPConfigData empty{};
399 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600400 return;
401 }
402 std::string service = resp.begin()->first;
403 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800404 [callback, ldapType](
405 const boost::system::error_code errorCode,
406 const dbus::utility::ManagedObjectType& ldapObjects) {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600407 LDAPConfigData confData{};
Ed Tanous81ce6092020-12-17 16:54:55 +0000408 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600409 {
410 callback(false, confData, ldapType);
411 BMCWEB_LOG_ERROR << "D-Bus responses error: "
Ed Tanous81ce6092020-12-17 16:54:55 +0000412 << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600413 return;
414 }
415
416 std::string ldapDbusType;
417 std::string searchString;
418
419 if (ldapType == "LDAP")
420 {
George Liu0fda0f12021-11-16 10:06:17 +0800421 ldapDbusType =
422 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600423 searchString = "openldap";
424 }
425 else if (ldapType == "ActiveDirectory")
426 {
427 ldapDbusType =
George Liu0fda0f12021-11-16 10:06:17 +0800428 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600429 searchString = "active_directory";
430 }
431 else
432 {
433 BMCWEB_LOG_ERROR
434 << "Can't get the DbusType for the given type="
435 << ldapType;
436 callback(false, confData, ldapType);
437 return;
438 }
439
440 std::string ldapEnableInterfaceStr = ldapEnableInterface;
441 std::string ldapConfigInterfaceStr = ldapConfigInterface;
442
443 for (const auto& object : ldapObjects)
444 {
445 // let's find the object whose ldap type is equal to the
446 // given type
447 if (object.first.str.find(searchString) ==
448 std::string::npos)
449 {
450 continue;
451 }
452
453 for (const auto& interface : object.second)
454 {
455 if (interface.first == ldapEnableInterfaceStr)
456 {
457 // rest of the properties are string.
458 for (const auto& property : interface.second)
459 {
460 if (property.first == "Enabled")
461 {
462 const bool* value =
463 std::get_if<bool>(&property.second);
464 if (value == nullptr)
465 {
466 continue;
467 }
468 confData.serviceEnabled = *value;
469 break;
470 }
471 }
472 }
473 else if (interface.first == ldapConfigInterfaceStr)
474 {
475
476 for (const auto& property : interface.second)
477 {
Ed Tanous271584a2019-07-09 16:24:22 -0700478 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600479 std::get_if<std::string>(
480 &property.second);
Ed Tanous271584a2019-07-09 16:24:22 -0700481 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600482 {
483 continue;
484 }
485 if (property.first == "LDAPServerURI")
486 {
Ed Tanous271584a2019-07-09 16:24:22 -0700487 confData.uri = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600488 }
489 else if (property.first == "LDAPBindDN")
490 {
Ed Tanous271584a2019-07-09 16:24:22 -0700491 confData.bindDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600492 }
493 else if (property.first == "LDAPBaseDN")
494 {
Ed Tanous271584a2019-07-09 16:24:22 -0700495 confData.baseDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600496 }
497 else if (property.first ==
498 "LDAPSearchScope")
499 {
Ed Tanous271584a2019-07-09 16:24:22 -0700500 confData.searchScope = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600501 }
502 else if (property.first ==
503 "GroupNameAttribute")
504 {
Ed Tanous271584a2019-07-09 16:24:22 -0700505 confData.groupAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600506 }
507 else if (property.first ==
508 "UserNameAttribute")
509 {
Ed Tanous271584a2019-07-09 16:24:22 -0700510 confData.userNameAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600511 }
512 else if (property.first == "LDAPType")
513 {
Ed Tanous271584a2019-07-09 16:24:22 -0700514 confData.serverType = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600515 }
516 }
517 }
George Liu0fda0f12021-11-16 10:06:17 +0800518 else if (
519 interface.first ==
520 "xyz.openbmc_project.User.PrivilegeMapperEntry")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600521 {
522 LDAPRoleMapData roleMapData{};
523 for (const auto& property : interface.second)
524 {
Ed Tanous271584a2019-07-09 16:24:22 -0700525 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600526 std::get_if<std::string>(
527 &property.second);
528
Ed Tanous271584a2019-07-09 16:24:22 -0700529 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600530 {
531 continue;
532 }
533
534 if (property.first == "GroupName")
535 {
Ed Tanous271584a2019-07-09 16:24:22 -0700536 roleMapData.groupName = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600537 }
538 else if (property.first == "Privilege")
539 {
Ed Tanous271584a2019-07-09 16:24:22 -0700540 roleMapData.privilege = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600541 }
542 }
543
Ed Tanous0f0353b2019-10-24 11:37:51 -0700544 confData.groupRoleList.emplace_back(
545 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600546 }
547 }
548 }
549 callback(true, confData, ldapType);
550 },
551 service, ldapRootObject, dbusObjManagerIntf,
552 "GetManagedObjects");
553 },
554 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
Ed Tanous23a21a12020-07-25 04:45:05 +0000555 ldapConfigObjectName, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530556}
557
Ed Tanous6c51eab2021-06-03 12:30:29 -0700558/**
559 * @brief parses the authentication section under the LDAP
560 * @param input JSON data
561 * @param asyncResp pointer to the JSON response
562 * @param userName userName to be filled from the given JSON.
563 * @param password password to be filled from the given JSON.
564 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700565inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700566 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
567 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700568{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700569 std::optional<std::string> authType;
570
571 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
572 authType, "Username", username, "Password",
573 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700575 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700577 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530578 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700579 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530580 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700581 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530582 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700583 messages::propertyValueNotInList(asyncResp->res, *authType,
584 "AuthenticationType");
585 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530586 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700587}
588/**
589 * @brief parses the LDAPService section under the LDAP
590 * @param input JSON data
591 * @param asyncResp pointer to the JSON response
592 * @param baseDNList baseDN to be filled from the given JSON.
593 * @param userNameAttribute userName to be filled from the given JSON.
594 * @param groupaAttribute password to be filled from the given JSON.
595 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530596
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700597inline void
598 parseLDAPServiceJson(nlohmann::json input,
599 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
600 std::optional<std::vector<std::string>>& baseDNList,
601 std::optional<std::string>& userNameAttribute,
602 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700603{
604 std::optional<nlohmann::json> searchSettings;
605
606 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
607 searchSettings))
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 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530612 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700613 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530614 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700615 if (!json_util::readJson(*searchSettings, asyncResp->res,
616 "BaseDistinguishedNames", baseDNList,
617 "UsernameAttribute", userNameAttribute,
618 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530619 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700620 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530621 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700622}
623/**
624 * @brief updates the LDAP server address and updates the
625 json response with the new value.
626 * @param serviceAddressList address to be updated.
627 * @param asyncResp pointer to the JSON response
628 * @param ldapServerElementName Type of LDAP
629 server(openLDAP/ActiveDirectory)
630 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530631
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700632inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700633 const std::vector<std::string>& serviceAddressList,
634 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
635 const std::string& ldapServerElementName,
636 const std::string& ldapConfigObject)
637{
638 crow::connections::systemBus->async_method_call(
639 [asyncResp, ldapServerElementName,
640 serviceAddressList](const boost::system::error_code ec) {
641 if (ec)
642 {
643 BMCWEB_LOG_DEBUG
644 << "Error Occurred in updating the service address";
645 messages::internalError(asyncResp->res);
646 return;
647 }
648 std::vector<std::string> modifiedserviceAddressList = {
649 serviceAddressList.front()};
650 asyncResp->res
651 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
652 modifiedserviceAddressList;
653 if ((serviceAddressList).size() > 1)
654 {
655 messages::propertyValueModified(asyncResp->res,
656 "ServiceAddresses",
657 serviceAddressList.front());
658 }
659 BMCWEB_LOG_DEBUG << "Updated the service address";
660 },
661 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
662 ldapConfigInterface, "LDAPServerURI",
Ed Tanous168e20c2021-12-13 14:39:53 -0800663 dbus::utility::DbusVariantType(serviceAddressList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700664}
665/**
666 * @brief updates the LDAP Bind DN and updates the
667 json response with the new value.
668 * @param username name of the user which needs to be updated.
669 * @param asyncResp pointer to the JSON response
670 * @param ldapServerElementName Type of LDAP
671 server(openLDAP/ActiveDirectory)
672 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530673
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700674inline void
675 handleUserNamePatch(const std::string& username,
676 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
677 const std::string& ldapServerElementName,
678 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700679{
680 crow::connections::systemBus->async_method_call(
681 [asyncResp, username,
682 ldapServerElementName](const boost::system::error_code ec) {
683 if (ec)
684 {
685 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
686 messages::internalError(asyncResp->res);
687 return;
688 }
689 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
690 ["Username"] = username;
691 BMCWEB_LOG_DEBUG << "Updated the username";
692 },
693 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800694 ldapConfigInterface, "LDAPBindDN",
695 dbus::utility::DbusVariantType(username));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700696}
697
698/**
699 * @brief updates the LDAP password
700 * @param password : ldap password which needs to be updated.
701 * @param asyncResp pointer to the JSON response
702 * @param ldapServerElementName Type of LDAP
703 * server(openLDAP/ActiveDirectory)
704 */
705
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700706inline void
707 handlePasswordPatch(const std::string& password,
708 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
709 const std::string& ldapServerElementName,
710 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700711{
712 crow::connections::systemBus->async_method_call(
713 [asyncResp, password,
714 ldapServerElementName](const boost::system::error_code ec) {
715 if (ec)
716 {
717 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
718 messages::internalError(asyncResp->res);
719 return;
720 }
721 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
722 ["Password"] = "";
723 BMCWEB_LOG_DEBUG << "Updated the password";
724 },
725 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
726 ldapConfigInterface, "LDAPBindDNPassword",
Ed Tanous168e20c2021-12-13 14:39:53 -0800727 dbus::utility::DbusVariantType(password));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700728}
729
730/**
731 * @brief updates the LDAP BaseDN and updates the
732 json response with the new value.
733 * @param baseDNList baseDN list which needs to be updated.
734 * @param asyncResp pointer to the JSON response
735 * @param ldapServerElementName Type of LDAP
736 server(openLDAP/ActiveDirectory)
737 */
738
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700739inline void
740 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
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, baseDNList,
747 ldapServerElementName](const boost::system::error_code ec) {
748 if (ec)
749 {
750 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
751 messages::internalError(asyncResp->res);
752 return;
753 }
754 auto& serverTypeJson =
755 asyncResp->res.jsonValue[ldapServerElementName];
756 auto& searchSettingsJson =
757 serverTypeJson["LDAPService"]["SearchSettings"];
758 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
759 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
760 if (baseDNList.size() > 1)
761 {
762 messages::propertyValueModified(asyncResp->res,
763 "BaseDistinguishedNames",
764 baseDNList.front());
765 }
766 BMCWEB_LOG_DEBUG << "Updated the base DN";
767 },
768 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
769 ldapConfigInterface, "LDAPBaseDN",
Ed Tanous168e20c2021-12-13 14:39:53 -0800770 dbus::utility::DbusVariantType(baseDNList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700771}
772/**
773 * @brief updates the LDAP user name attribute and updates the
774 json response with the new value.
775 * @param userNameAttribute attribute to be updated.
776 * @param asyncResp pointer to the JSON response
777 * @param ldapServerElementName Type of LDAP
778 server(openLDAP/ActiveDirectory)
779 */
780
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700781inline void
782 handleUserNameAttrPatch(const std::string& userNameAttribute,
783 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
784 const std::string& ldapServerElementName,
785 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700786{
787 crow::connections::systemBus->async_method_call(
788 [asyncResp, userNameAttribute,
789 ldapServerElementName](const boost::system::error_code ec) {
790 if (ec)
791 {
792 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
793 "username attribute";
794 messages::internalError(asyncResp->res);
795 return;
796 }
797 auto& serverTypeJson =
798 asyncResp->res.jsonValue[ldapServerElementName];
799 auto& searchSettingsJson =
800 serverTypeJson["LDAPService"]["SearchSettings"];
801 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
802 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
803 },
804 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
805 ldapConfigInterface, "UserNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800806 dbus::utility::DbusVariantType(userNameAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700807}
808/**
809 * @brief updates the LDAP group attribute and updates the
810 json response with the new value.
811 * @param groupsAttribute attribute to be updated.
812 * @param asyncResp pointer to the JSON response
813 * @param ldapServerElementName Type of LDAP
814 server(openLDAP/ActiveDirectory)
815 */
816
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700817inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700818 const std::string& groupsAttribute,
819 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
820 const std::string& ldapServerElementName,
821 const std::string& ldapConfigObject)
822{
823 crow::connections::systemBus->async_method_call(
824 [asyncResp, groupsAttribute,
825 ldapServerElementName](const boost::system::error_code ec) {
826 if (ec)
827 {
828 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
829 "groupname attribute";
830 messages::internalError(asyncResp->res);
831 return;
832 }
833 auto& serverTypeJson =
834 asyncResp->res.jsonValue[ldapServerElementName];
835 auto& searchSettingsJson =
836 serverTypeJson["LDAPService"]["SearchSettings"];
837 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
838 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
839 },
840 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
841 ldapConfigInterface, "GroupNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800842 dbus::utility::DbusVariantType(groupsAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700843}
844/**
845 * @brief updates the LDAP service enable and updates the
846 json response with the new value.
847 * @param input JSON data.
848 * @param asyncResp pointer to the JSON response
849 * @param ldapServerElementName Type of LDAP
850 server(openLDAP/ActiveDirectory)
851 */
852
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700853inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700854 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
855 const std::string& ldapServerElementName,
856 const std::string& ldapConfigObject)
857{
858 crow::connections::systemBus->async_method_call(
859 [asyncResp, serviceEnabled,
860 ldapServerElementName](const boost::system::error_code ec) {
861 if (ec)
862 {
863 BMCWEB_LOG_DEBUG
864 << "Error Occurred in Updating the service enable";
865 messages::internalError(asyncResp->res);
866 return;
867 }
868 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
869 serviceEnabled;
870 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
871 },
872 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800873 ldapEnableInterface, "Enabled",
874 dbus::utility::DbusVariantType(serviceEnabled));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700875}
876
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700877inline void
878 handleAuthMethodsPatch(nlohmann::json& input,
879 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700880{
881 std::optional<bool> basicAuth;
882 std::optional<bool> cookie;
883 std::optional<bool> sessionToken;
884 std::optional<bool> xToken;
885 std::optional<bool> tls;
886
887 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
888 "Cookie", cookie, "SessionToken", sessionToken,
889 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530890 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700891 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
892 return;
893 }
894
895 // Make a copy of methods configuration
896 persistent_data::AuthConfigMethods authMethodsConfig =
897 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
898
899 if (basicAuth)
900 {
901#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
902 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800903 asyncResp->res,
904 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700905 return;
906#endif
907 authMethodsConfig.basic = *basicAuth;
908 }
909
910 if (cookie)
911 {
912#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800913 messages::actionNotSupported(
914 asyncResp->res,
915 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700916 return;
917#endif
918 authMethodsConfig.cookie = *cookie;
919 }
920
921 if (sessionToken)
922 {
923#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
924 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800925 asyncResp->res,
926 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700927 return;
928#endif
929 authMethodsConfig.sessionToken = *sessionToken;
930 }
931
932 if (xToken)
933 {
934#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800935 messages::actionNotSupported(
936 asyncResp->res,
937 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700938 return;
939#endif
940 authMethodsConfig.xtoken = *xToken;
941 }
942
943 if (tls)
944 {
945#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800946 messages::actionNotSupported(
947 asyncResp->res,
948 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700949 return;
950#endif
951 authMethodsConfig.tls = *tls;
952 }
953
954 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
955 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
956 !authMethodsConfig.tls)
957 {
958 // Do not allow user to disable everything
959 messages::actionNotSupported(asyncResp->res,
960 "of disabling all available methods");
961 return;
962 }
963
964 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
965 authMethodsConfig);
966 // Save configuration immediately
967 persistent_data::getConfig().writeData();
968
969 messages::success(asyncResp->res);
970}
971
972/**
973 * @brief Get the required values from the given JSON, validates the
974 * value and create the LDAP config object.
975 * @param input JSON data
976 * @param asyncResp pointer to the JSON response
977 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
978 */
979
980inline void handleLDAPPatch(nlohmann::json& input,
981 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
982 const std::string& serverType)
983{
984 std::string dbusObjectPath;
985 if (serverType == "ActiveDirectory")
986 {
987 dbusObjectPath = adConfigObject;
988 }
989 else if (serverType == "LDAP")
990 {
991 dbusObjectPath = ldapConfigObjectName;
992 }
993 else
994 {
995 return;
996 }
997
998 std::optional<nlohmann::json> authentication;
999 std::optional<nlohmann::json> ldapService;
1000 std::optional<std::vector<std::string>> serviceAddressList;
1001 std::optional<bool> serviceEnabled;
1002 std::optional<std::vector<std::string>> baseDNList;
1003 std::optional<std::string> userNameAttribute;
1004 std::optional<std::string> groupsAttribute;
1005 std::optional<std::string> userName;
1006 std::optional<std::string> password;
1007 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1008
1009 if (!json_util::readJson(input, asyncResp->res, "Authentication",
1010 authentication, "LDAPService", ldapService,
1011 "ServiceAddresses", serviceAddressList,
1012 "ServiceEnabled", serviceEnabled,
1013 "RemoteRoleMapping", remoteRoleMapData))
1014 {
1015 return;
1016 }
1017
1018 if (authentication)
1019 {
1020 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1021 password);
1022 }
1023 if (ldapService)
1024 {
1025 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1026 userNameAttribute, groupsAttribute);
1027 }
1028 if (serviceAddressList)
1029 {
Ed Tanous26f69762022-01-25 09:49:11 -08001030 if (serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301031 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001032 messages::propertyValueNotInList(asyncResp->res, "[]",
1033 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001034 return;
1035 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001036 }
1037 if (baseDNList)
1038 {
Ed Tanous26f69762022-01-25 09:49:11 -08001039 if (baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +05301040 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001041 messages::propertyValueNotInList(asyncResp->res, "[]",
1042 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301043 return;
1044 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001045 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301046
Ed Tanous6c51eab2021-06-03 12:30:29 -07001047 // nothing to update, then return
1048 if (!userName && !password && !serviceAddressList && !baseDNList &&
1049 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1050 !remoteRoleMapData)
1051 {
1052 return;
1053 }
1054
1055 // Get the existing resource first then keep modifying
1056 // whenever any property gets updated.
1057 getLDAPConfigData(serverType, [asyncResp, userName, password, baseDNList,
1058 userNameAttribute, groupsAttribute,
1059 serviceAddressList, serviceEnabled,
1060 dbusObjectPath, remoteRoleMapData](
1061 bool success,
1062 const LDAPConfigData& confData,
1063 const std::string& serverT) {
1064 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301065 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001066 messages::internalError(asyncResp->res);
1067 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301068 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001069 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1070 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301071 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001072 // Disable the service first and update the rest of
1073 // the properties.
1074 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301075 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001076
Ratan Gupta8a07d282019-03-16 08:33:47 +05301077 if (serviceAddressList)
1078 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001079 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1080 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301081 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001082 if (userName)
1083 {
1084 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1085 }
1086 if (password)
1087 {
1088 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1089 }
1090
Ratan Gupta8a07d282019-03-16 08:33:47 +05301091 if (baseDNList)
1092 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001093 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1094 }
1095 if (userNameAttribute)
1096 {
1097 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1098 dbusObjectPath);
1099 }
1100 if (groupsAttribute)
1101 {
1102 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1103 dbusObjectPath);
1104 }
1105 if (serviceEnabled)
1106 {
1107 // if user has given the value as true then enable
1108 // the service. if user has given false then no-op
1109 // as service is already stopped.
1110 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301111 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001112 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1113 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301114 }
1115 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001116 else
1117 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001118 // if user has not given the service enabled value
1119 // then revert it to the same state as it was
1120 // before.
1121 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1122 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001123 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001124
Ed Tanous6c51eab2021-06-03 12:30:29 -07001125 if (remoteRoleMapData)
1126 {
1127 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1128 *remoteRoleMapData);
1129 }
1130 });
1131}
1132
1133inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1134 const std::string& username,
1135 std::optional<std::string> password,
1136 std::optional<bool> enabled,
1137 std::optional<std::string> roleId,
1138 std::optional<bool> locked)
1139{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301140 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1141 tempObjPath /= username;
1142 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001143
1144 dbus::utility::checkDbusPathExists(
1145 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001146 [dbusObjectPath, username, password(std::move(password)),
1147 roleId(std::move(roleId)), enabled, locked,
1148 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001149 if (!rc)
1150 {
1151 messages::resourceNotFound(
1152 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1153 username);
1154 return;
1155 }
1156
1157 if (password)
1158 {
1159 int retval = pamUpdatePassword(username, *password);
1160
1161 if (retval == PAM_USER_UNKNOWN)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001162 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001163 messages::resourceNotFound(
1164 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1165 username);
1166 }
1167 else if (retval == PAM_AUTHTOK_ERR)
1168 {
1169 // If password is invalid
1170 messages::propertyValueFormatError(asyncResp->res,
1171 *password, "Password");
1172 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1173 }
1174 else if (retval != PAM_SUCCESS)
1175 {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001176 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001177 return;
1178 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001179 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001180
Ed Tanous6c51eab2021-06-03 12:30:29 -07001181 if (enabled)
1182 {
1183 crow::connections::systemBus->async_method_call(
1184 [asyncResp](const boost::system::error_code ec) {
1185 if (ec)
1186 {
1187 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1188 messages::internalError(asyncResp->res);
1189 return;
1190 }
1191 messages::success(asyncResp->res);
1192 return;
1193 },
1194 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1195 "org.freedesktop.DBus.Properties", "Set",
1196 "xyz.openbmc_project.User.Attributes", "UserEnabled",
Ed Tanous168e20c2021-12-13 14:39:53 -08001197 dbus::utility::DbusVariantType{*enabled});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001198 }
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001199
Ed Tanous6c51eab2021-06-03 12:30:29 -07001200 if (roleId)
1201 {
1202 std::string priv = getPrivilegeFromRoleId(*roleId);
1203 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001204 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001205 messages::propertyValueNotInList(asyncResp->res, *roleId,
1206 "RoleId");
1207 return;
1208 }
1209 if (priv == "priv-noaccess")
1210 {
1211 priv = "";
1212 }
1213
1214 crow::connections::systemBus->async_method_call(
1215 [asyncResp](const boost::system::error_code ec) {
1216 if (ec)
1217 {
1218 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1219 messages::internalError(asyncResp->res);
1220 return;
1221 }
1222 messages::success(asyncResp->res);
1223 },
1224 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1225 "org.freedesktop.DBus.Properties", "Set",
1226 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
Ed Tanous168e20c2021-12-13 14:39:53 -08001227 dbus::utility::DbusVariantType{priv});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001228 }
1229
1230 if (locked)
1231 {
1232 // admin can unlock the account which is locked by
1233 // successive authentication failures but admin should
1234 // not be allowed to lock an account.
1235 if (*locked)
1236 {
1237 messages::propertyValueNotInList(asyncResp->res, "true",
1238 "Locked");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001239 return;
1240 }
1241
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001242 crow::connections::systemBus->async_method_call(
Ed Tanous6c51eab2021-06-03 12:30:29 -07001243 [asyncResp](const boost::system::error_code ec) {
1244 if (ec)
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001245 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001246 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1247 messages::internalError(asyncResp->res);
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001248 return;
1249 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001250 messages::success(asyncResp->res);
1251 return;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001252 },
Ed Tanous6c51eab2021-06-03 12:30:29 -07001253 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1254 "org.freedesktop.DBus.Properties", "Set",
1255 "xyz.openbmc_project.User.Attributes",
Ed Tanous168e20c2021-12-13 14:39:53 -08001256 "UserLockedForFailedAttempt",
1257 dbus::utility::DbusVariantType{*locked});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001258 }
1259 });
1260}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001261
Ed Tanous6c51eab2021-06-03 12:30:29 -07001262inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001263{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001264
Ed Tanous6c51eab2021-06-03 12:30:29 -07001265 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001266 .privileges(redfish::privileges::getAccountService)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001267 .methods(
Abhishek Patel72048782021-06-02 09:53:24 -05001268 boost::beast::http::verb::get)([](const crow::Request& req,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001269 const std::shared_ptr<
1270 bmcweb::AsyncResp>& asyncResp)
1271 -> void {
1272 const persistent_data::AuthConfigMethods& authMethodsConfig =
1273 persistent_data::SessionStore::getInstance()
1274 .getAuthMethodsConfig();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001275
Ed Tanous6c51eab2021-06-03 12:30:29 -07001276 asyncResp->res.jsonValue = {
1277 {"@odata.id", "/redfish/v1/AccountService"},
1278 {"@odata.type", "#AccountService."
1279 "v1_5_0.AccountService"},
1280 {"Id", "AccountService"},
1281 {"Name", "Account Service"},
1282 {"Description", "Account Service"},
1283 {"ServiceEnabled", true},
1284 {"MaxPasswordLength", 20},
1285 {"Accounts",
1286 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
1287 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
1288 {"Oem",
1289 {{"OpenBMC",
1290 {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
Ed Tanousd8e3c292021-09-21 18:01:43 -07001291 {"@odata.id", "/redfish/v1/AccountService#/Oem/OpenBMC"},
Ed Tanous6c51eab2021-06-03 12:30:29 -07001292 {"AuthMethods",
1293 {
1294 {"BasicAuth", authMethodsConfig.basic},
1295 {"SessionToken", authMethodsConfig.sessionToken},
1296 {"XToken", authMethodsConfig.xtoken},
1297 {"Cookie", authMethodsConfig.cookie},
1298 {"TLS", authMethodsConfig.tls},
Abhishek Patel72048782021-06-02 09:53:24 -05001299 }}}}}}};
1300 // /redfish/v1/AccountService/LDAP/Certificates is something only
1301 // ConfigureManager can access then only display when the user has
1302 // permissions ConfigureManager
1303 Privileges effectiveUserPrivileges =
1304 redfish::getUserPrivileges(req.userRole);
1305
1306 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1307 effectiveUserPrivileges))
1308 {
1309 asyncResp->res.jsonValue["LDAP"] = {
1310 {"Certificates",
1311 {{"@odata.id",
1312 "/redfish/v1/AccountService/LDAP/Certificates"}}}};
1313 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001314 crow::connections::systemBus->async_method_call(
1315 [asyncResp](
1316 const boost::system::error_code ec,
1317 const std::vector<
Ed Tanous168e20c2021-12-13 14:39:53 -08001318 std::pair<std::string, dbus::utility::DbusVariantType>>&
Ed Tanous6c51eab2021-06-03 12:30:29 -07001319 propertiesList) {
1320 if (ec)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001321 {
1322 messages::internalError(asyncResp->res);
1323 return;
1324 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001325 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1326 << "properties for AccountService";
1327 for (const std::pair<std::string,
Ed Tanous168e20c2021-12-13 14:39:53 -08001328 dbus::utility::DbusVariantType>&
1329 property : propertiesList)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001330 {
1331 if (property.first == "MinPasswordLength")
1332 {
1333 const uint8_t* value =
1334 std::get_if<uint8_t>(&property.second);
1335 if (value != nullptr)
Ratan Gupta24c85422019-01-30 19:41:24 +05301336 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001337 asyncResp->res.jsonValue["MinPasswordLength"] =
1338 *value;
Ratan Gupta24c85422019-01-30 19:41:24 +05301339 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001340 }
1341 if (property.first == "AccountUnlockTimeout")
1342 {
1343 const uint32_t* value =
1344 std::get_if<uint32_t>(&property.second);
1345 if (value != nullptr)
1346 {
1347 asyncResp->res
1348 .jsonValue["AccountLockoutDuration"] =
1349 *value;
1350 }
1351 }
1352 if (property.first == "MaxLoginAttemptBeforeLockout")
1353 {
1354 const uint16_t* value =
1355 std::get_if<uint16_t>(&property.second);
1356 if (value != nullptr)
1357 {
1358 asyncResp->res
1359 .jsonValue["AccountLockoutThreshold"] =
1360 *value;
1361 }
1362 }
1363 }
1364 },
1365 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1366 "org.freedesktop.DBus.Properties", "GetAll",
1367 "xyz.openbmc_project.User.AccountPolicy");
1368
1369 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
1370 const std::string& ldapType) {
1371 if (!success)
1372 {
1373 return;
1374 }
1375 parseLDAPConfigData(asyncResp->res.jsonValue, confData,
1376 ldapType);
1377 };
1378
1379 getLDAPConfigData("LDAP", callback);
1380 getLDAPConfigData("ActiveDirectory", callback);
1381 });
1382
Ed Tanousf5ffd802021-07-19 10:55:33 -07001383 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06001384 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07001385 .methods(boost::beast::http::verb::patch)(
1386 [](const crow::Request& req,
1387 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1388 std::optional<uint32_t> unlockTimeout;
1389 std::optional<uint16_t> lockoutThreshold;
Paul Fertseref73ad02022-01-21 19:44:40 +00001390 std::optional<uint8_t> minPasswordLength;
Ed Tanousf5ffd802021-07-19 10:55:33 -07001391 std::optional<uint16_t> maxPasswordLength;
1392 std::optional<nlohmann::json> ldapObject;
1393 std::optional<nlohmann::json> activeDirectoryObject;
1394 std::optional<nlohmann::json> oemObject;
1395
1396 if (!json_util::readJson(
1397 req, asyncResp->res, "AccountLockoutDuration",
1398 unlockTimeout, "AccountLockoutThreshold",
1399 lockoutThreshold, "MaxPasswordLength",
1400 maxPasswordLength, "MinPasswordLength",
1401 minPasswordLength, "LDAP", ldapObject,
1402 "ActiveDirectory", activeDirectoryObject, "Oem",
1403 oemObject))
1404 {
1405 return;
1406 }
1407
1408 if (minPasswordLength)
1409 {
Paul Fertseref73ad02022-01-21 19:44:40 +00001410 crow::connections::systemBus->async_method_call(
1411 [asyncResp](const boost::system::error_code ec) {
1412 if (ec)
1413 {
1414 messages::internalError(asyncResp->res);
1415 return;
1416 }
1417 messages::success(asyncResp->res);
1418 },
1419 "xyz.openbmc_project.User.Manager",
1420 "/xyz/openbmc_project/user",
1421 "org.freedesktop.DBus.Properties", "Set",
1422 "xyz.openbmc_project.User.AccountPolicy",
1423 "MinPasswordLength",
1424 dbus::utility::DbusVariantType(*minPasswordLength));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001425 }
1426
1427 if (maxPasswordLength)
1428 {
1429 messages::propertyNotWritable(asyncResp->res,
1430 "MaxPasswordLength");
1431 }
1432
1433 if (ldapObject)
1434 {
1435 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1436 }
1437
1438 if (std::optional<nlohmann::json> oemOpenBMCObject;
1439 oemObject &&
1440 json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1441 oemOpenBMCObject))
1442 {
1443 if (std::optional<nlohmann::json> authMethodsObject;
1444 oemOpenBMCObject &&
1445 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1446 "AuthMethods", authMethodsObject))
1447 {
1448 if (authMethodsObject)
1449 {
1450 handleAuthMethodsPatch(*authMethodsObject,
1451 asyncResp);
1452 }
1453 }
1454 }
1455
1456 if (activeDirectoryObject)
1457 {
1458 handleLDAPPatch(*activeDirectoryObject, asyncResp,
1459 "ActiveDirectory");
1460 }
1461
1462 if (unlockTimeout)
1463 {
1464 crow::connections::systemBus->async_method_call(
1465 [asyncResp](const boost::system::error_code ec) {
1466 if (ec)
1467 {
1468 messages::internalError(asyncResp->res);
1469 return;
1470 }
1471 messages::success(asyncResp->res);
1472 },
1473 "xyz.openbmc_project.User.Manager",
1474 "/xyz/openbmc_project/user",
1475 "org.freedesktop.DBus.Properties", "Set",
1476 "xyz.openbmc_project.User.AccountPolicy",
1477 "AccountUnlockTimeout",
Ed Tanous168e20c2021-12-13 14:39:53 -08001478 dbus::utility::DbusVariantType(*unlockTimeout));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001479 }
1480 if (lockoutThreshold)
1481 {
1482 crow::connections::systemBus->async_method_call(
1483 [asyncResp](const boost::system::error_code ec) {
1484 if (ec)
1485 {
1486 messages::internalError(asyncResp->res);
1487 return;
1488 }
1489 messages::success(asyncResp->res);
1490 },
1491 "xyz.openbmc_project.User.Manager",
1492 "/xyz/openbmc_project/user",
1493 "org.freedesktop.DBus.Properties", "Set",
1494 "xyz.openbmc_project.User.AccountPolicy",
1495 "MaxLoginAttemptBeforeLockout",
Ed Tanous168e20c2021-12-13 14:39:53 -08001496 dbus::utility::DbusVariantType(*lockoutThreshold));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001497 }
1498 });
1499
Ed Tanous6c51eab2021-06-03 12:30:29 -07001500 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001501 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001502 .methods(boost::beast::http::verb::get)(
1503 [](const crow::Request& req,
1504 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1505 asyncResp->res.jsonValue = {
1506 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
1507 {"@odata.type", "#ManagerAccountCollection."
1508 "ManagerAccountCollection"},
1509 {"Name", "Accounts Collection"},
1510 {"Description", "BMC User Accounts"}};
1511
Ed Tanous6c51eab2021-06-03 12:30:29 -07001512 Privileges effectiveUserPrivileges =
1513 redfish::getUserPrivileges(req.userRole);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001514
JunLin Chenf5e29f32021-12-08 16:47:04 +08001515 std::string thisUser;
1516 if (req.session)
1517 {
1518 thisUser = req.session->username;
1519 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001520 crow::connections::systemBus->async_method_call(
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001521 [asyncResp, thisUser, effectiveUserPrivileges](
1522 const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001523 const dbus::utility::ManagedObjectType& users) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001524 if (ec)
1525 {
1526 messages::internalError(asyncResp->res);
Ratan Gupta24c85422019-01-30 19:41:24 +05301527 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001528 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001529
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001530 bool userCanSeeAllAccounts =
1531 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001532 {"ConfigureUsers"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001533
1534 bool userCanSeeSelf =
1535 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001536 {"ConfigureSelf"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001537
Ed Tanous6c51eab2021-06-03 12:30:29 -07001538 nlohmann::json& memberArray =
1539 asyncResp->res.jsonValue["Members"];
1540 memberArray = nlohmann::json::array();
Ratan Gupta24c85422019-01-30 19:41:24 +05301541
Ed Tanous9eb808c2022-01-25 10:19:23 -08001542 for (const auto& userpath : users)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001543 {
1544 std::string user = userpath.first.filename();
1545 if (user.empty())
Ratan Gupta24c85422019-01-30 19:41:24 +05301546 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301547 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001548 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1549
Ratan Gupta24c85422019-01-30 19:41:24 +05301550 return;
1551 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301552
Ed Tanous6c51eab2021-06-03 12:30:29 -07001553 // As clarified by Redfish here:
1554 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1555 // Users without ConfigureUsers, only see their own
1556 // account. Users with ConfigureUsers, see all
1557 // accounts.
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001558 if (userCanSeeAllAccounts ||
1559 (thisUser == user && userCanSeeSelf))
Ratan Gupta24c85422019-01-30 19:41:24 +05301560 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001561 memberArray.push_back(
1562 {{"@odata.id",
1563 "/redfish/v1/AccountService/Accounts/" +
1564 user}});
Ratan Gupta24c85422019-01-30 19:41:24 +05301565 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001566 }
1567 asyncResp->res.jsonValue["Members@odata.count"] =
1568 memberArray.size();
1569 },
1570 "xyz.openbmc_project.User.Manager",
1571 "/xyz/openbmc_project/user",
1572 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Ratan Gupta24c85422019-01-30 19:41:24 +05301573 });
Ed Tanous06e086d2018-09-19 17:19:52 -07001574
Ed Tanous6c51eab2021-06-03 12:30:29 -07001575 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001576 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001577 .methods(boost::beast::http::verb::post)([](const crow::Request& req,
1578 const std::shared_ptr<
1579 bmcweb::AsyncResp>&
1580 asyncResp) -> void {
1581 std::string username;
1582 std::string password;
1583 std::optional<std::string> roleId("User");
1584 std::optional<bool> enabled = true;
1585 if (!json_util::readJson(req, asyncResp->res, "UserName", username,
1586 "Password", password, "RoleId", roleId,
1587 "Enabled", enabled))
1588 {
1589 return;
1590 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001591
Ed Tanous6c51eab2021-06-03 12:30:29 -07001592 std::string priv = getPrivilegeFromRoleId(*roleId);
1593 if (priv.empty())
1594 {
1595 messages::propertyValueNotInList(asyncResp->res, *roleId,
1596 "RoleId");
1597 return;
1598 }
1599 // TODO: Following override will be reverted once support in
1600 // phosphor-user-manager is added. In order to avoid dependency
1601 // issues, this is added in bmcweb, which will removed, once
1602 // phosphor-user-manager supports priv-noaccess.
1603 if (priv == "priv-noaccess")
1604 {
1605 roleId = "";
1606 }
1607 else
1608 {
1609 roleId = priv;
1610 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001611
Ed Tanous6c51eab2021-06-03 12:30:29 -07001612 // Reading AllGroups property
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001613 sdbusplus::asio::getProperty<std::vector<std::string>>(
1614 *crow::connections::systemBus,
1615 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1616 "xyz.openbmc_project.User.Manager", "AllGroups",
Ed Tanous6c51eab2021-06-03 12:30:29 -07001617 [asyncResp, username, password{std::move(password)}, roleId,
Ed Tanous168e20c2021-12-13 14:39:53 -08001618 enabled](const boost::system::error_code ec,
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001619 const std::vector<std::string>& allGroupsList) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001620 if (ec)
1621 {
1622 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1623 messages::internalError(asyncResp->res);
1624 return;
1625 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001626
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001627 if (allGroupsList.empty())
Ed Tanous6c51eab2021-06-03 12:30:29 -07001628 {
1629 messages::internalError(asyncResp->res);
1630 return;
1631 }
1632
1633 crow::connections::systemBus->async_method_call(
1634 [asyncResp, username,
1635 password](const boost::system::error_code ec2,
1636 sdbusplus::message::message& m) {
1637 if (ec2)
1638 {
1639 userErrorMessageHandler(
1640 m.get_error(), asyncResp, username, "");
1641 return;
1642 }
1643
1644 if (pamUpdatePassword(username, password) !=
1645 PAM_SUCCESS)
1646 {
1647 // At this point we have a user that's been
1648 // created, but the password set
1649 // failed.Something is wrong, so delete the user
1650 // that we've already created
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301651 sdbusplus::message::object_path tempObjPath(
1652 rootUserDbusPath);
1653 tempObjPath /= username;
1654 const std::string userPath(tempObjPath);
1655
Ed Tanous6c51eab2021-06-03 12:30:29 -07001656 crow::connections::systemBus->async_method_call(
1657 [asyncResp, password](
1658 const boost::system::error_code ec3) {
1659 if (ec3)
1660 {
1661 messages::internalError(
1662 asyncResp->res);
1663 return;
1664 }
1665
1666 // If password is invalid
1667 messages::propertyValueFormatError(
1668 asyncResp->res, password,
1669 "Password");
1670 },
1671 "xyz.openbmc_project.User.Manager",
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301672 userPath,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001673 "xyz.openbmc_project.Object.Delete",
1674 "Delete");
1675
1676 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1677 return;
1678 }
1679
1680 messages::created(asyncResp->res);
1681 asyncResp->res.addHeader(
1682 "Location",
1683 "/redfish/v1/AccountService/Accounts/" +
1684 username);
1685 },
1686 "xyz.openbmc_project.User.Manager",
1687 "/xyz/openbmc_project/user",
1688 "xyz.openbmc_project.User.Manager", "CreateUser",
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001689 username, allGroupsList, *roleId, *enabled);
1690 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001691 });
1692
1693 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001694 .privileges(redfish::privileges::getManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001695 .methods(
1696 boost::beast::http::verb::
1697 get)([](const crow::Request& req,
1698 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1699 const std::string& accountName) -> void {
1700 if (req.session->username != accountName)
1701 {
1702 // At this point we've determined that the user is trying to
1703 // modify a user that isn't them. We need to verify that they
1704 // have permissions to modify other users, so re-run the auth
1705 // check with the same permissions, minus ConfigureSelf.
1706 Privileges effectiveUserPrivileges =
1707 redfish::getUserPrivileges(req.userRole);
1708 Privileges requiredPermissionsToChangeNonSelf = {
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001709 "ConfigureUsers", "ConfigureManager"};
Ed Tanous6c51eab2021-06-03 12:30:29 -07001710 if (!effectiveUserPrivileges.isSupersetOf(
1711 requiredPermissionsToChangeNonSelf))
Ed Tanous06e086d2018-09-19 17:19:52 -07001712 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001713 BMCWEB_LOG_DEBUG << "GET Account denied access";
1714 messages::insufficientPrivilege(asyncResp->res);
1715 return;
1716 }
1717 }
1718
1719 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -08001720 [asyncResp,
1721 accountName](const boost::system::error_code ec,
1722 const dbus::utility::ManagedObjectType& users) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001723 if (ec)
1724 {
1725 messages::internalError(asyncResp->res);
1726 return;
1727 }
Ed Tanous711ac7a2021-12-20 09:34:41 -08001728 const auto userIt = std::find_if(
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301729 users.begin(), users.end(),
1730 [accountName](
1731 const std::pair<sdbusplus::message::object_path,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001732 dbus::utility::DBusInteracesMap>&
1733 user) {
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301734 return !accountName.compare(user.first.filename());
1735 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001736
Ed Tanous6c51eab2021-06-03 12:30:29 -07001737 if (userIt == users.end())
1738 {
1739 messages::resourceNotFound(
1740 asyncResp->res, "ManagerAccount", accountName);
1741 return;
1742 }
1743
1744 asyncResp->res.jsonValue = {
1745 {"@odata.type",
1746 "#ManagerAccount.v1_4_0.ManagerAccount"},
1747 {"Name", "User Account"},
1748 {"Description", "User Account"},
1749 {"Password", nullptr},
1750 {"AccountTypes", {"Redfish"}}};
1751
1752 for (const auto& interface : userIt->second)
1753 {
1754 if (interface.first ==
1755 "xyz.openbmc_project.User.Attributes")
1756 {
1757 for (const auto& property : interface.second)
1758 {
1759 if (property.first == "UserEnabled")
1760 {
1761 const bool* userEnabled =
1762 std::get_if<bool>(&property.second);
1763 if (userEnabled == nullptr)
1764 {
1765 BMCWEB_LOG_ERROR
1766 << "UserEnabled wasn't a bool";
1767 messages::internalError(asyncResp->res);
1768 return;
1769 }
1770 asyncResp->res.jsonValue["Enabled"] =
1771 *userEnabled;
1772 }
1773 else if (property.first ==
1774 "UserLockedForFailedAttempt")
1775 {
1776 const bool* userLocked =
1777 std::get_if<bool>(&property.second);
1778 if (userLocked == nullptr)
1779 {
1780 BMCWEB_LOG_ERROR << "UserLockedForF"
1781 "ailedAttempt "
1782 "wasn't a bool";
1783 messages::internalError(asyncResp->res);
1784 return;
1785 }
1786 asyncResp->res.jsonValue["Locked"] =
1787 *userLocked;
1788 asyncResp->res.jsonValue
1789 ["Locked@Redfish.AllowableValues"] = {
1790 "false"}; // can only unlock accounts
1791 }
1792 else if (property.first == "UserPrivilege")
1793 {
1794 const std::string* userPrivPtr =
1795 std::get_if<std::string>(
1796 &property.second);
1797 if (userPrivPtr == nullptr)
1798 {
1799 BMCWEB_LOG_ERROR
1800 << "UserPrivilege wasn't a "
1801 "string";
1802 messages::internalError(asyncResp->res);
1803 return;
1804 }
1805 std::string role =
1806 getRoleIdFromPrivilege(*userPrivPtr);
1807 if (role.empty())
1808 {
1809 BMCWEB_LOG_ERROR << "Invalid user role";
1810 messages::internalError(asyncResp->res);
1811 return;
1812 }
1813 asyncResp->res.jsonValue["RoleId"] = role;
1814
1815 asyncResp->res.jsonValue["Links"]["Role"] =
1816 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +08001817 "/redfish/v1/AccountService/Roles/" +
Ed Tanous6c51eab2021-06-03 12:30:29 -07001818 role}};
1819 }
1820 else if (property.first ==
1821 "UserPasswordExpired")
1822 {
1823 const bool* userPasswordExpired =
1824 std::get_if<bool>(&property.second);
1825 if (userPasswordExpired == nullptr)
1826 {
George Liu0fda0f12021-11-16 10:06:17 +08001827 BMCWEB_LOG_ERROR
1828 << "UserPasswordExpired wasn't a bool";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001829 messages::internalError(asyncResp->res);
1830 return;
1831 }
1832 asyncResp->res
1833 .jsonValue["PasswordChangeRequired"] =
1834 *userPasswordExpired;
1835 }
1836 }
1837 }
1838 }
1839
1840 asyncResp->res.jsonValue["@odata.id"] =
1841 "/redfish/v1/AccountService/Accounts/" + accountName;
1842 asyncResp->res.jsonValue["Id"] = accountName;
1843 asyncResp->res.jsonValue["UserName"] = accountName;
1844 },
1845 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1846 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1847 });
1848
1849 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001850 // TODO this privilege should be using the generated endpoints, but
1851 // because of the special handling of ConfigureSelf, it's not able to
1852 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001853 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1854 .methods(boost::beast::http::verb::patch)(
1855 [](const crow::Request& req,
1856 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1857 const std::string& username) -> void {
1858 std::optional<std::string> newUserName;
1859 std::optional<std::string> password;
1860 std::optional<bool> enabled;
1861 std::optional<std::string> roleId;
1862 std::optional<bool> locked;
Ed Tanouse9cc5172021-11-03 14:13:19 +08001863
1864 Privileges effectiveUserPrivileges =
1865 redfish::getUserPrivileges(req.userRole);
1866 Privileges configureUsers = {"ConfigureUsers"};
1867 bool userHasConfigureUsers =
1868 effectiveUserPrivileges.isSupersetOf(configureUsers);
1869 if (userHasConfigureUsers)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001870 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001871 // Users with ConfigureUsers can modify for all users
1872 if (!json_util::readJson(req, asyncResp->res, "UserName",
1873 newUserName, "Password", password,
1874 "RoleId", roleId, "Enabled",
1875 enabled, "Locked", locked))
1876 {
1877 return;
1878 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001879 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001880 else
Ed Tanous6c51eab2021-06-03 12:30:29 -07001881 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001882 // ConfigureSelf accounts can only modify their own account
1883 if (username != req.session->username)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001884 {
1885 messages::insufficientPrivilege(asyncResp->res);
1886 return;
1887 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001888 // ConfigureSelf accounts can only modify their password
1889 if (!json_util::readJson(req, asyncResp->res, "Password",
1890 password))
1891 {
1892 return;
1893 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001894 }
1895
1896 // if user name is not provided in the patch method or if it
1897 // matches the user name in the URI, then we are treating it as
1898 // updating user properties other then username. If username
1899 // provided doesn't match the URI, then we are treating this as
1900 // user rename request.
1901 if (!newUserName || (newUserName.value() == username))
1902 {
1903 updateUserProperties(asyncResp, username, password, enabled,
1904 roleId, locked);
1905 return;
1906 }
1907 crow::connections::systemBus->async_method_call(
1908 [asyncResp, username, password(std::move(password)),
1909 roleId(std::move(roleId)), enabled,
1910 newUser{std::string(*newUserName)},
1911 locked](const boost::system::error_code ec,
1912 sdbusplus::message::message& m) {
1913 if (ec)
1914 {
1915 userErrorMessageHandler(m.get_error(), asyncResp,
1916 newUser, username);
1917 return;
1918 }
1919
1920 updateUserProperties(asyncResp, newUser, password,
1921 enabled, roleId, locked);
1922 },
1923 "xyz.openbmc_project.User.Manager",
1924 "/xyz/openbmc_project/user",
1925 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1926 *newUserName);
1927 });
1928
1929 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001930 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001931 .methods(boost::beast::http::verb::delete_)(
1932 [](const crow::Request& /*req*/,
1933 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1934 const std::string& username) -> void {
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301935 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1936 tempObjPath /= username;
1937 const std::string userPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001938
1939 crow::connections::systemBus->async_method_call(
1940 [asyncResp, username](const boost::system::error_code ec) {
1941 if (ec)
1942 {
1943 messages::resourceNotFound(
1944 asyncResp->res,
1945 "#ManagerAccount.v1_4_0.ManagerAccount",
1946 username);
1947 return;
1948 }
1949
1950 messages::accountRemoved(asyncResp->res);
1951 },
1952 "xyz.openbmc_project.User.Manager", userPath,
1953 "xyz.openbmc_project.Object.Delete", "Delete");
1954 });
1955}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001956
Ed Tanous1abe55e2018-09-05 08:30:59 -07001957} // namespace redfish