blob: b746cef29707cec885d2e9796edcad11820b51f6 [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
74using ManagedObjectType =
75 std::vector<std::pair<sdbusplus::message::object_path, DbusInterfaceType>>;
76
Ratan Gupta6973a582018-12-13 18:25:44 +053077using GetObjectType =
78 std::vector<std::pair<std::string, std::vector<std::string>>>;
AppaRao Puli84e12cb2018-10-11 01:28:15 +053079
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060080inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053081{
82 if (role == "priv-admin")
83 {
84 return "Administrator";
85 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070086 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053087 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053088 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053089 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070090 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053091 {
92 return "Operator";
93 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070094 if ((role == "") || (role == "priv-noaccess"))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +000095 {
96 return "NoAccess";
97 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +053098 return "";
99}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600100inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530101{
102 if (role == "Administrator")
103 {
104 return "priv-admin";
105 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700106 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530107 {
108 return "priv-user";
109 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700110 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530111 {
112 return "priv-operator";
113 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700114 if ((role == "NoAccess") || (role == ""))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +0000115 {
116 return "priv-noaccess";
117 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530118 return "";
119}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700120
zhanghch058d1b46d2021-04-01 11:18:24 +0800121inline void userErrorMessageHandler(
122 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
123 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000124{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000125 if (e == nullptr)
126 {
127 messages::internalError(asyncResp->res);
128 return;
129 }
130
Manojkiran Eda055806b2020-11-03 09:36:28 +0530131 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000132 if (strcmp(errorMessage,
133 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
134 {
135 messages::resourceAlreadyExists(asyncResp->res,
Gunnar Mills8114bd42020-06-11 20:55:21 -0500136 "#ManagerAccount.v1_4_0.ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000137 "UserName", newUser);
138 }
139 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
140 "UserNameDoesNotExist") == 0)
141 {
142 messages::resourceNotFound(
Gunnar Mills8114bd42020-06-11 20:55:21 -0500143 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000144 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700145 else if ((strcmp(errorMessage,
146 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
147 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800148 (strcmp(
149 errorMessage,
150 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
151 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000152 {
153 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
154 }
155 else if (strcmp(errorMessage,
156 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
157 {
158 messages::createLimitReachedForResource(asyncResp->res);
159 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000160 else
161 {
162 messages::internalError(asyncResp->res);
163 }
164
165 return;
166}
167
Ed Tanous81ce6092020-12-17 16:54:55 +0000168inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000169 const LDAPConfigData& confData,
170 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530171{
Ratan Guptaab828d72019-04-22 14:18:33 +0530172 std::string service =
173 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600174 nlohmann::json ldap = {
Ratan Gupta6973a582018-12-13 18:25:44 +0530175 {"ServiceEnabled", confData.serviceEnabled},
176 {"ServiceAddresses", nlohmann::json::array({confData.uri})},
177 {"Authentication",
178 {{"AuthenticationType", "UsernameAndPassword"},
Ratan Gupta6973a582018-12-13 18:25:44 +0530179 {"Username", confData.bindDN},
180 {"Password", nullptr}}},
181 {"LDAPService",
182 {{"SearchSettings",
183 {{"BaseDistinguishedNames",
184 nlohmann::json::array({confData.baseDN})},
185 {"UsernameAttribute", confData.userNameAttribute},
186 {"GroupsAttribute", confData.groupAttribute}}}}},
187 };
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600188
Ed Tanous81ce6092020-12-17 16:54:55 +0000189 jsonResponse[ldapType].update(ldap);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600190
Ed Tanous81ce6092020-12-17 16:54:55 +0000191 nlohmann::json& roleMapArray = jsonResponse[ldapType]["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600192 roleMapArray = nlohmann::json::array();
193 for (auto& obj : confData.groupRoleList)
194 {
195 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
196 << obj.second.groupName << "\n";
197 roleMapArray.push_back(
198 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
199 nlohmann::json::array(
200 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
201 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530202}
203
204/**
Ratan Gupta06785242019-07-26 22:30:16 +0530205 * @brief validates given JSON input and then calls appropriate method to
206 * create, to delete or to set Rolemapping object based on the given input.
207 *
208 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000209inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800210 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530211 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700212 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530213{
214 for (size_t index = 0; index < input.size(); index++)
215 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700216 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530217
218 if (thisJson.is_null())
219 {
220 // delete the existing object
221 if (index < roleMapObjData.size())
222 {
223 crow::connections::systemBus->async_method_call(
224 [asyncResp, roleMapObjData, serverType,
225 index](const boost::system::error_code ec) {
226 if (ec)
227 {
228 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
229 messages::internalError(asyncResp->res);
230 return;
231 }
232 asyncResp->res
233 .jsonValue[serverType]["RemoteRoleMapping"][index] =
234 nullptr;
235 },
236 ldapDbusService, roleMapObjData[index].first,
237 "xyz.openbmc_project.Object.Delete", "Delete");
238 }
239 else
240 {
241 BMCWEB_LOG_ERROR << "Can't delete the object";
242 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800243 asyncResp->res,
244 thisJson.dump(2, ' ', true,
245 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530246 "RemoteRoleMapping/" + std::to_string(index));
247 return;
248 }
249 }
250 else if (thisJson.empty())
251 {
252 // Don't do anything for the empty objects,parse next json
253 // eg {"RemoteRoleMapping",[{}]}
254 }
255 else
256 {
257 // update/create the object
258 std::optional<std::string> remoteGroup;
259 std::optional<std::string> localRole;
260
Ed Tanousf23b7292020-10-15 09:41:17 -0700261 // This is a copy, but it's required in this case because of how
262 // readJson is structured
263 nlohmann::json thisJsonCopy = thisJson;
264 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
265 "RemoteGroup", remoteGroup, "LocalRole",
266 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530267 {
268 continue;
269 }
270
271 // Update existing RoleMapping Object
272 if (index < roleMapObjData.size())
273 {
274 BMCWEB_LOG_DEBUG << "Update Role Map Object";
275 // If "RemoteGroup" info is provided
276 if (remoteGroup)
277 {
278 crow::connections::systemBus->async_method_call(
279 [asyncResp, roleMapObjData, serverType, index,
280 remoteGroup](const boost::system::error_code ec) {
281 if (ec)
282 {
283 BMCWEB_LOG_ERROR << "DBUS response error: "
284 << ec;
285 messages::internalError(asyncResp->res);
286 return;
287 }
288 asyncResp->res
289 .jsonValue[serverType]["RemoteRoleMapping"]
290 [index]["RemoteGroup"] = *remoteGroup;
291 },
292 ldapDbusService, roleMapObjData[index].first,
293 propertyInterface, "Set",
294 "xyz.openbmc_project.User.PrivilegeMapperEntry",
295 "GroupName",
Ed Tanous168e20c2021-12-13 14:39:53 -0800296 dbus::utility::DbusVariantType(
297 std::move(*remoteGroup)));
Ratan Gupta06785242019-07-26 22:30:16 +0530298 }
299
300 // If "LocalRole" info is provided
301 if (localRole)
302 {
303 crow::connections::systemBus->async_method_call(
304 [asyncResp, roleMapObjData, serverType, index,
305 localRole](const boost::system::error_code ec) {
306 if (ec)
307 {
308 BMCWEB_LOG_ERROR << "DBUS response error: "
309 << ec;
310 messages::internalError(asyncResp->res);
311 return;
312 }
313 asyncResp->res
314 .jsonValue[serverType]["RemoteRoleMapping"]
315 [index]["LocalRole"] = *localRole;
316 },
317 ldapDbusService, roleMapObjData[index].first,
318 propertyInterface, "Set",
319 "xyz.openbmc_project.User.PrivilegeMapperEntry",
320 "Privilege",
Ed Tanous168e20c2021-12-13 14:39:53 -0800321 dbus::utility::DbusVariantType(
Ratan Gupta06785242019-07-26 22:30:16 +0530322 getPrivilegeFromRoleId(std::move(*localRole))));
323 }
324 }
325 // Create a new RoleMapping Object.
326 else
327 {
328 BMCWEB_LOG_DEBUG
329 << "setRoleMappingProperties: Creating new Object";
330 std::string pathString =
331 "RemoteRoleMapping/" + std::to_string(index);
332
333 if (!localRole)
334 {
335 messages::propertyMissing(asyncResp->res,
336 pathString + "/LocalRole");
337 continue;
338 }
339 if (!remoteGroup)
340 {
341 messages::propertyMissing(asyncResp->res,
342 pathString + "/RemoteGroup");
343 continue;
344 }
345
346 std::string dbusObjectPath;
347 if (serverType == "ActiveDirectory")
348 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700349 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530350 }
351 else if (serverType == "LDAP")
352 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000353 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530354 }
355
356 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
357 << ",LocalRole=" << *localRole;
358
359 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700360 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530361 remoteGroup](const boost::system::error_code ec) {
362 if (ec)
363 {
364 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
365 messages::internalError(asyncResp->res);
366 return;
367 }
368 nlohmann::json& remoteRoleJson =
369 asyncResp->res
370 .jsonValue[serverType]["RemoteRoleMapping"];
371 remoteRoleJson.push_back(
372 {{"LocalRole", *localRole},
373 {"RemoteGroup", *remoteGroup}});
374 },
375 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700376 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530377 getPrivilegeFromRoleId(std::move(*localRole)));
378 }
379 }
380 }
381}
382
383/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530384 * Function that retrieves all properties for LDAP config object
385 * into JSON
386 */
387template <typename CallbackFunc>
388inline void getLDAPConfigData(const std::string& ldapType,
389 CallbackFunc&& callback)
390{
Ratan Guptaab828d72019-04-22 14:18:33 +0530391
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600392 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530393 ldapConfigInterface};
394
395 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600396 [callback, ldapType](const boost::system::error_code ec,
397 const GetObjectType& resp) {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600398 if (ec || resp.empty())
399 {
George Liu0fda0f12021-11-16 10:06:17 +0800400 BMCWEB_LOG_ERROR
401 << "DBUS response error during getting of service name: "
402 << ec;
Ed Tanous23a21a12020-07-25 04:45:05 +0000403 LDAPConfigData empty{};
404 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600405 return;
406 }
407 std::string service = resp.begin()->first;
408 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000409 [callback, ldapType](const boost::system::error_code errorCode,
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600410 const ManagedObjectType& ldapObjects) {
411 LDAPConfigData confData{};
Ed Tanous81ce6092020-12-17 16:54:55 +0000412 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600413 {
414 callback(false, confData, ldapType);
415 BMCWEB_LOG_ERROR << "D-Bus responses error: "
Ed Tanous81ce6092020-12-17 16:54:55 +0000416 << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600417 return;
418 }
419
420 std::string ldapDbusType;
421 std::string searchString;
422
423 if (ldapType == "LDAP")
424 {
George Liu0fda0f12021-11-16 10:06:17 +0800425 ldapDbusType =
426 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600427 searchString = "openldap";
428 }
429 else if (ldapType == "ActiveDirectory")
430 {
431 ldapDbusType =
George Liu0fda0f12021-11-16 10:06:17 +0800432 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600433 searchString = "active_directory";
434 }
435 else
436 {
437 BMCWEB_LOG_ERROR
438 << "Can't get the DbusType for the given type="
439 << ldapType;
440 callback(false, confData, ldapType);
441 return;
442 }
443
444 std::string ldapEnableInterfaceStr = ldapEnableInterface;
445 std::string ldapConfigInterfaceStr = ldapConfigInterface;
446
447 for (const auto& object : ldapObjects)
448 {
449 // let's find the object whose ldap type is equal to the
450 // given type
451 if (object.first.str.find(searchString) ==
452 std::string::npos)
453 {
454 continue;
455 }
456
457 for (const auto& interface : object.second)
458 {
459 if (interface.first == ldapEnableInterfaceStr)
460 {
461 // rest of the properties are string.
462 for (const auto& property : interface.second)
463 {
464 if (property.first == "Enabled")
465 {
466 const bool* value =
467 std::get_if<bool>(&property.second);
468 if (value == nullptr)
469 {
470 continue;
471 }
472 confData.serviceEnabled = *value;
473 break;
474 }
475 }
476 }
477 else if (interface.first == ldapConfigInterfaceStr)
478 {
479
480 for (const auto& property : interface.second)
481 {
Ed Tanous271584a2019-07-09 16:24:22 -0700482 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600483 std::get_if<std::string>(
484 &property.second);
Ed Tanous271584a2019-07-09 16:24:22 -0700485 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600486 {
487 continue;
488 }
489 if (property.first == "LDAPServerURI")
490 {
Ed Tanous271584a2019-07-09 16:24:22 -0700491 confData.uri = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600492 }
493 else if (property.first == "LDAPBindDN")
494 {
Ed Tanous271584a2019-07-09 16:24:22 -0700495 confData.bindDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600496 }
497 else if (property.first == "LDAPBaseDN")
498 {
Ed Tanous271584a2019-07-09 16:24:22 -0700499 confData.baseDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600500 }
501 else if (property.first ==
502 "LDAPSearchScope")
503 {
Ed Tanous271584a2019-07-09 16:24:22 -0700504 confData.searchScope = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600505 }
506 else if (property.first ==
507 "GroupNameAttribute")
508 {
Ed Tanous271584a2019-07-09 16:24:22 -0700509 confData.groupAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600510 }
511 else if (property.first ==
512 "UserNameAttribute")
513 {
Ed Tanous271584a2019-07-09 16:24:22 -0700514 confData.userNameAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600515 }
516 else if (property.first == "LDAPType")
517 {
Ed Tanous271584a2019-07-09 16:24:22 -0700518 confData.serverType = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600519 }
520 }
521 }
George Liu0fda0f12021-11-16 10:06:17 +0800522 else if (
523 interface.first ==
524 "xyz.openbmc_project.User.PrivilegeMapperEntry")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600525 {
526 LDAPRoleMapData roleMapData{};
527 for (const auto& property : interface.second)
528 {
Ed Tanous271584a2019-07-09 16:24:22 -0700529 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600530 std::get_if<std::string>(
531 &property.second);
532
Ed Tanous271584a2019-07-09 16:24:22 -0700533 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600534 {
535 continue;
536 }
537
538 if (property.first == "GroupName")
539 {
Ed Tanous271584a2019-07-09 16:24:22 -0700540 roleMapData.groupName = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600541 }
542 else if (property.first == "Privilege")
543 {
Ed Tanous271584a2019-07-09 16:24:22 -0700544 roleMapData.privilege = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600545 }
546 }
547
Ed Tanous0f0353b2019-10-24 11:37:51 -0700548 confData.groupRoleList.emplace_back(
549 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600550 }
551 }
552 }
553 callback(true, confData, ldapType);
554 },
555 service, ldapRootObject, dbusObjManagerIntf,
556 "GetManagedObjects");
557 },
558 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
Ed Tanous23a21a12020-07-25 04:45:05 +0000559 ldapConfigObjectName, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530560}
561
Ed Tanous6c51eab2021-06-03 12:30:29 -0700562/**
563 * @brief parses the authentication section under the LDAP
564 * @param input JSON data
565 * @param asyncResp pointer to the JSON response
566 * @param userName userName to be filled from the given JSON.
567 * @param password password to be filled from the given JSON.
568 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700569inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700570 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
571 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700573 std::optional<std::string> authType;
574
575 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
576 authType, "Username", username, "Password",
577 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700579 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700581 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530582 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700583 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530584 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700585 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530586 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700587 messages::propertyValueNotInList(asyncResp->res, *authType,
588 "AuthenticationType");
589 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530590 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700591}
592/**
593 * @brief parses the LDAPService section under the LDAP
594 * @param input JSON data
595 * @param asyncResp pointer to the JSON response
596 * @param baseDNList baseDN to be filled from the given JSON.
597 * @param userNameAttribute userName to be filled from the given JSON.
598 * @param groupaAttribute password to be filled from the given JSON.
599 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530600
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700601inline void
602 parseLDAPServiceJson(nlohmann::json input,
603 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
604 std::optional<std::vector<std::string>>& baseDNList,
605 std::optional<std::string>& userNameAttribute,
606 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700607{
608 std::optional<nlohmann::json> searchSettings;
609
610 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
611 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 (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530616 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700617 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530618 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700619 if (!json_util::readJson(*searchSettings, asyncResp->res,
620 "BaseDistinguishedNames", baseDNList,
621 "UsernameAttribute", userNameAttribute,
622 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530623 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700624 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530625 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700626}
627/**
628 * @brief updates the LDAP server address and updates the
629 json response with the new value.
630 * @param serviceAddressList address to be updated.
631 * @param asyncResp pointer to the JSON response
632 * @param ldapServerElementName Type of LDAP
633 server(openLDAP/ActiveDirectory)
634 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530635
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700636inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700637 const std::vector<std::string>& serviceAddressList,
638 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
639 const std::string& ldapServerElementName,
640 const std::string& ldapConfigObject)
641{
642 crow::connections::systemBus->async_method_call(
643 [asyncResp, ldapServerElementName,
644 serviceAddressList](const boost::system::error_code ec) {
645 if (ec)
646 {
647 BMCWEB_LOG_DEBUG
648 << "Error Occurred in updating the service address";
649 messages::internalError(asyncResp->res);
650 return;
651 }
652 std::vector<std::string> modifiedserviceAddressList = {
653 serviceAddressList.front()};
654 asyncResp->res
655 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
656 modifiedserviceAddressList;
657 if ((serviceAddressList).size() > 1)
658 {
659 messages::propertyValueModified(asyncResp->res,
660 "ServiceAddresses",
661 serviceAddressList.front());
662 }
663 BMCWEB_LOG_DEBUG << "Updated the service address";
664 },
665 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
666 ldapConfigInterface, "LDAPServerURI",
Ed Tanous168e20c2021-12-13 14:39:53 -0800667 dbus::utility::DbusVariantType(serviceAddressList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700668}
669/**
670 * @brief updates the LDAP Bind DN and updates the
671 json response with the new value.
672 * @param username name of the user which needs to be updated.
673 * @param asyncResp pointer to the JSON response
674 * @param ldapServerElementName Type of LDAP
675 server(openLDAP/ActiveDirectory)
676 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530677
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700678inline void
679 handleUserNamePatch(const std::string& username,
680 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
681 const std::string& ldapServerElementName,
682 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700683{
684 crow::connections::systemBus->async_method_call(
685 [asyncResp, username,
686 ldapServerElementName](const boost::system::error_code ec) {
687 if (ec)
688 {
689 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
690 messages::internalError(asyncResp->res);
691 return;
692 }
693 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
694 ["Username"] = username;
695 BMCWEB_LOG_DEBUG << "Updated the username";
696 },
697 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800698 ldapConfigInterface, "LDAPBindDN",
699 dbus::utility::DbusVariantType(username));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700700}
701
702/**
703 * @brief updates the LDAP password
704 * @param password : ldap password which needs to be updated.
705 * @param asyncResp pointer to the JSON response
706 * @param ldapServerElementName Type of LDAP
707 * server(openLDAP/ActiveDirectory)
708 */
709
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700710inline void
711 handlePasswordPatch(const std::string& password,
712 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
713 const std::string& ldapServerElementName,
714 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700715{
716 crow::connections::systemBus->async_method_call(
717 [asyncResp, password,
718 ldapServerElementName](const boost::system::error_code ec) {
719 if (ec)
720 {
721 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
722 messages::internalError(asyncResp->res);
723 return;
724 }
725 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
726 ["Password"] = "";
727 BMCWEB_LOG_DEBUG << "Updated the password";
728 },
729 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
730 ldapConfigInterface, "LDAPBindDNPassword",
Ed Tanous168e20c2021-12-13 14:39:53 -0800731 dbus::utility::DbusVariantType(password));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700732}
733
734/**
735 * @brief updates the LDAP BaseDN and updates the
736 json response with the new value.
737 * @param baseDNList baseDN list which needs to be updated.
738 * @param asyncResp pointer to the JSON response
739 * @param ldapServerElementName Type of LDAP
740 server(openLDAP/ActiveDirectory)
741 */
742
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700743inline void
744 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
745 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
746 const std::string& ldapServerElementName,
747 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700748{
749 crow::connections::systemBus->async_method_call(
750 [asyncResp, baseDNList,
751 ldapServerElementName](const boost::system::error_code ec) {
752 if (ec)
753 {
754 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
755 messages::internalError(asyncResp->res);
756 return;
757 }
758 auto& serverTypeJson =
759 asyncResp->res.jsonValue[ldapServerElementName];
760 auto& searchSettingsJson =
761 serverTypeJson["LDAPService"]["SearchSettings"];
762 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
763 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
764 if (baseDNList.size() > 1)
765 {
766 messages::propertyValueModified(asyncResp->res,
767 "BaseDistinguishedNames",
768 baseDNList.front());
769 }
770 BMCWEB_LOG_DEBUG << "Updated the base DN";
771 },
772 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
773 ldapConfigInterface, "LDAPBaseDN",
Ed Tanous168e20c2021-12-13 14:39:53 -0800774 dbus::utility::DbusVariantType(baseDNList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700775}
776/**
777 * @brief updates the LDAP user name attribute and updates the
778 json response with the new value.
779 * @param userNameAttribute attribute to be updated.
780 * @param asyncResp pointer to the JSON response
781 * @param ldapServerElementName Type of LDAP
782 server(openLDAP/ActiveDirectory)
783 */
784
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700785inline void
786 handleUserNameAttrPatch(const std::string& userNameAttribute,
787 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
788 const std::string& ldapServerElementName,
789 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700790{
791 crow::connections::systemBus->async_method_call(
792 [asyncResp, userNameAttribute,
793 ldapServerElementName](const boost::system::error_code ec) {
794 if (ec)
795 {
796 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
797 "username attribute";
798 messages::internalError(asyncResp->res);
799 return;
800 }
801 auto& serverTypeJson =
802 asyncResp->res.jsonValue[ldapServerElementName];
803 auto& searchSettingsJson =
804 serverTypeJson["LDAPService"]["SearchSettings"];
805 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
806 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
807 },
808 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
809 ldapConfigInterface, "UserNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800810 dbus::utility::DbusVariantType(userNameAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700811}
812/**
813 * @brief updates the LDAP group attribute and updates the
814 json response with the new value.
815 * @param groupsAttribute attribute to be updated.
816 * @param asyncResp pointer to the JSON response
817 * @param ldapServerElementName Type of LDAP
818 server(openLDAP/ActiveDirectory)
819 */
820
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700821inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700822 const std::string& groupsAttribute,
823 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
824 const std::string& ldapServerElementName,
825 const std::string& ldapConfigObject)
826{
827 crow::connections::systemBus->async_method_call(
828 [asyncResp, groupsAttribute,
829 ldapServerElementName](const boost::system::error_code ec) {
830 if (ec)
831 {
832 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
833 "groupname attribute";
834 messages::internalError(asyncResp->res);
835 return;
836 }
837 auto& serverTypeJson =
838 asyncResp->res.jsonValue[ldapServerElementName];
839 auto& searchSettingsJson =
840 serverTypeJson["LDAPService"]["SearchSettings"];
841 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
842 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
843 },
844 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
845 ldapConfigInterface, "GroupNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800846 dbus::utility::DbusVariantType(groupsAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700847}
848/**
849 * @brief updates the LDAP service enable and updates the
850 json response with the new value.
851 * @param input JSON data.
852 * @param asyncResp pointer to the JSON response
853 * @param ldapServerElementName Type of LDAP
854 server(openLDAP/ActiveDirectory)
855 */
856
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700857inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700858 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
859 const std::string& ldapServerElementName,
860 const std::string& ldapConfigObject)
861{
862 crow::connections::systemBus->async_method_call(
863 [asyncResp, serviceEnabled,
864 ldapServerElementName](const boost::system::error_code ec) {
865 if (ec)
866 {
867 BMCWEB_LOG_DEBUG
868 << "Error Occurred in Updating the service enable";
869 messages::internalError(asyncResp->res);
870 return;
871 }
872 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
873 serviceEnabled;
874 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
875 },
876 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800877 ldapEnableInterface, "Enabled",
878 dbus::utility::DbusVariantType(serviceEnabled));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700879}
880
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700881inline void
882 handleAuthMethodsPatch(nlohmann::json& input,
883 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700884{
885 std::optional<bool> basicAuth;
886 std::optional<bool> cookie;
887 std::optional<bool> sessionToken;
888 std::optional<bool> xToken;
889 std::optional<bool> tls;
890
891 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
892 "Cookie", cookie, "SessionToken", sessionToken,
893 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530894 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700895 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
896 return;
897 }
898
899 // Make a copy of methods configuration
900 persistent_data::AuthConfigMethods authMethodsConfig =
901 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
902
903 if (basicAuth)
904 {
905#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
906 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800907 asyncResp->res,
908 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700909 return;
910#endif
911 authMethodsConfig.basic = *basicAuth;
912 }
913
914 if (cookie)
915 {
916#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800917 messages::actionNotSupported(
918 asyncResp->res,
919 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700920 return;
921#endif
922 authMethodsConfig.cookie = *cookie;
923 }
924
925 if (sessionToken)
926 {
927#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
928 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800929 asyncResp->res,
930 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700931 return;
932#endif
933 authMethodsConfig.sessionToken = *sessionToken;
934 }
935
936 if (xToken)
937 {
938#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800939 messages::actionNotSupported(
940 asyncResp->res,
941 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700942 return;
943#endif
944 authMethodsConfig.xtoken = *xToken;
945 }
946
947 if (tls)
948 {
949#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800950 messages::actionNotSupported(
951 asyncResp->res,
952 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700953 return;
954#endif
955 authMethodsConfig.tls = *tls;
956 }
957
958 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
959 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
960 !authMethodsConfig.tls)
961 {
962 // Do not allow user to disable everything
963 messages::actionNotSupported(asyncResp->res,
964 "of disabling all available methods");
965 return;
966 }
967
968 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
969 authMethodsConfig);
970 // Save configuration immediately
971 persistent_data::getConfig().writeData();
972
973 messages::success(asyncResp->res);
974}
975
976/**
977 * @brief Get the required values from the given JSON, validates the
978 * value and create the LDAP config object.
979 * @param input JSON data
980 * @param asyncResp pointer to the JSON response
981 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
982 */
983
984inline void handleLDAPPatch(nlohmann::json& input,
985 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
986 const std::string& serverType)
987{
988 std::string dbusObjectPath;
989 if (serverType == "ActiveDirectory")
990 {
991 dbusObjectPath = adConfigObject;
992 }
993 else if (serverType == "LDAP")
994 {
995 dbusObjectPath = ldapConfigObjectName;
996 }
997 else
998 {
999 return;
1000 }
1001
1002 std::optional<nlohmann::json> authentication;
1003 std::optional<nlohmann::json> ldapService;
1004 std::optional<std::vector<std::string>> serviceAddressList;
1005 std::optional<bool> serviceEnabled;
1006 std::optional<std::vector<std::string>> baseDNList;
1007 std::optional<std::string> userNameAttribute;
1008 std::optional<std::string> groupsAttribute;
1009 std::optional<std::string> userName;
1010 std::optional<std::string> password;
1011 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1012
1013 if (!json_util::readJson(input, asyncResp->res, "Authentication",
1014 authentication, "LDAPService", ldapService,
1015 "ServiceAddresses", serviceAddressList,
1016 "ServiceEnabled", serviceEnabled,
1017 "RemoteRoleMapping", remoteRoleMapData))
1018 {
1019 return;
1020 }
1021
1022 if (authentication)
1023 {
1024 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1025 password);
1026 }
1027 if (ldapService)
1028 {
1029 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1030 userNameAttribute, groupsAttribute);
1031 }
1032 if (serviceAddressList)
1033 {
1034 if ((*serviceAddressList).size() == 0)
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301035 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001036 messages::propertyValueNotInList(asyncResp->res, "[]",
1037 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001038 return;
1039 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001040 }
1041 if (baseDNList)
1042 {
1043 if ((*baseDNList).size() == 0)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301044 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001045 messages::propertyValueNotInList(asyncResp->res, "[]",
1046 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301047 return;
1048 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001049 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301050
Ed Tanous6c51eab2021-06-03 12:30:29 -07001051 // nothing to update, then return
1052 if (!userName && !password && !serviceAddressList && !baseDNList &&
1053 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1054 !remoteRoleMapData)
1055 {
1056 return;
1057 }
1058
1059 // Get the existing resource first then keep modifying
1060 // whenever any property gets updated.
1061 getLDAPConfigData(serverType, [asyncResp, userName, password, baseDNList,
1062 userNameAttribute, groupsAttribute,
1063 serviceAddressList, serviceEnabled,
1064 dbusObjectPath, remoteRoleMapData](
1065 bool success,
1066 const LDAPConfigData& confData,
1067 const std::string& serverT) {
1068 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301069 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001070 messages::internalError(asyncResp->res);
1071 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301072 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001073 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1074 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301075 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001076 // Disable the service first and update the rest of
1077 // the properties.
1078 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301079 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001080
Ratan Gupta8a07d282019-03-16 08:33:47 +05301081 if (serviceAddressList)
1082 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001083 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1084 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301085 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001086 if (userName)
1087 {
1088 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1089 }
1090 if (password)
1091 {
1092 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1093 }
1094
Ratan Gupta8a07d282019-03-16 08:33:47 +05301095 if (baseDNList)
1096 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001097 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1098 }
1099 if (userNameAttribute)
1100 {
1101 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1102 dbusObjectPath);
1103 }
1104 if (groupsAttribute)
1105 {
1106 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1107 dbusObjectPath);
1108 }
1109 if (serviceEnabled)
1110 {
1111 // if user has given the value as true then enable
1112 // the service. if user has given false then no-op
1113 // as service is already stopped.
1114 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301115 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001116 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1117 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301118 }
1119 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001120 else
1121 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001122 // if user has not given the service enabled value
1123 // then revert it to the same state as it was
1124 // before.
1125 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1126 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001127 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001128
Ed Tanous6c51eab2021-06-03 12:30:29 -07001129 if (remoteRoleMapData)
1130 {
1131 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1132 *remoteRoleMapData);
1133 }
1134 });
1135}
1136
1137inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1138 const std::string& username,
1139 std::optional<std::string> password,
1140 std::optional<bool> enabled,
1141 std::optional<std::string> roleId,
1142 std::optional<bool> locked)
1143{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301144 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1145 tempObjPath /= username;
1146 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001147
1148 dbus::utility::checkDbusPathExists(
1149 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001150 [dbusObjectPath, username, password(std::move(password)),
1151 roleId(std::move(roleId)), enabled, locked,
1152 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001153 if (!rc)
1154 {
1155 messages::resourceNotFound(
1156 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1157 username);
1158 return;
1159 }
1160
1161 if (password)
1162 {
1163 int retval = pamUpdatePassword(username, *password);
1164
1165 if (retval == PAM_USER_UNKNOWN)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001166 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001167 messages::resourceNotFound(
1168 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1169 username);
1170 }
1171 else if (retval == PAM_AUTHTOK_ERR)
1172 {
1173 // If password is invalid
1174 messages::propertyValueFormatError(asyncResp->res,
1175 *password, "Password");
1176 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1177 }
1178 else if (retval != PAM_SUCCESS)
1179 {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001180 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001181 return;
1182 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001183 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001184
Ed Tanous6c51eab2021-06-03 12:30:29 -07001185 if (enabled)
1186 {
1187 crow::connections::systemBus->async_method_call(
1188 [asyncResp](const boost::system::error_code ec) {
1189 if (ec)
1190 {
1191 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1192 messages::internalError(asyncResp->res);
1193 return;
1194 }
1195 messages::success(asyncResp->res);
1196 return;
1197 },
1198 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1199 "org.freedesktop.DBus.Properties", "Set",
1200 "xyz.openbmc_project.User.Attributes", "UserEnabled",
Ed Tanous168e20c2021-12-13 14:39:53 -08001201 dbus::utility::DbusVariantType{*enabled});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001202 }
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001203
Ed Tanous6c51eab2021-06-03 12:30:29 -07001204 if (roleId)
1205 {
1206 std::string priv = getPrivilegeFromRoleId(*roleId);
1207 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001208 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001209 messages::propertyValueNotInList(asyncResp->res, *roleId,
1210 "RoleId");
1211 return;
1212 }
1213 if (priv == "priv-noaccess")
1214 {
1215 priv = "";
1216 }
1217
1218 crow::connections::systemBus->async_method_call(
1219 [asyncResp](const boost::system::error_code ec) {
1220 if (ec)
1221 {
1222 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1223 messages::internalError(asyncResp->res);
1224 return;
1225 }
1226 messages::success(asyncResp->res);
1227 },
1228 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1229 "org.freedesktop.DBus.Properties", "Set",
1230 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
Ed Tanous168e20c2021-12-13 14:39:53 -08001231 dbus::utility::DbusVariantType{priv});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001232 }
1233
1234 if (locked)
1235 {
1236 // admin can unlock the account which is locked by
1237 // successive authentication failures but admin should
1238 // not be allowed to lock an account.
1239 if (*locked)
1240 {
1241 messages::propertyValueNotInList(asyncResp->res, "true",
1242 "Locked");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001243 return;
1244 }
1245
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001246 crow::connections::systemBus->async_method_call(
Ed Tanous6c51eab2021-06-03 12:30:29 -07001247 [asyncResp](const boost::system::error_code ec) {
1248 if (ec)
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001249 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001250 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1251 messages::internalError(asyncResp->res);
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001252 return;
1253 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001254 messages::success(asyncResp->res);
1255 return;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001256 },
Ed Tanous6c51eab2021-06-03 12:30:29 -07001257 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1258 "org.freedesktop.DBus.Properties", "Set",
1259 "xyz.openbmc_project.User.Attributes",
Ed Tanous168e20c2021-12-13 14:39:53 -08001260 "UserLockedForFailedAttempt",
1261 dbus::utility::DbusVariantType{*locked});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001262 }
1263 });
1264}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001265
Ed Tanous6c51eab2021-06-03 12:30:29 -07001266inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001267{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001268
Ed Tanous6c51eab2021-06-03 12:30:29 -07001269 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001270 .privileges(redfish::privileges::getAccountService)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001271 .methods(
Abhishek Patel72048782021-06-02 09:53:24 -05001272 boost::beast::http::verb::get)([](const crow::Request& req,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001273 const std::shared_ptr<
1274 bmcweb::AsyncResp>& asyncResp)
1275 -> void {
1276 const persistent_data::AuthConfigMethods& authMethodsConfig =
1277 persistent_data::SessionStore::getInstance()
1278 .getAuthMethodsConfig();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001279
Ed Tanous6c51eab2021-06-03 12:30:29 -07001280 asyncResp->res.jsonValue = {
1281 {"@odata.id", "/redfish/v1/AccountService"},
1282 {"@odata.type", "#AccountService."
1283 "v1_5_0.AccountService"},
1284 {"Id", "AccountService"},
1285 {"Name", "Account Service"},
1286 {"Description", "Account Service"},
1287 {"ServiceEnabled", true},
1288 {"MaxPasswordLength", 20},
1289 {"Accounts",
1290 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
1291 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
1292 {"Oem",
1293 {{"OpenBMC",
1294 {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
Ed Tanousd8e3c292021-09-21 18:01:43 -07001295 {"@odata.id", "/redfish/v1/AccountService#/Oem/OpenBMC"},
Ed Tanous6c51eab2021-06-03 12:30:29 -07001296 {"AuthMethods",
1297 {
1298 {"BasicAuth", authMethodsConfig.basic},
1299 {"SessionToken", authMethodsConfig.sessionToken},
1300 {"XToken", authMethodsConfig.xtoken},
1301 {"Cookie", authMethodsConfig.cookie},
1302 {"TLS", authMethodsConfig.tls},
Abhishek Patel72048782021-06-02 09:53:24 -05001303 }}}}}}};
1304 // /redfish/v1/AccountService/LDAP/Certificates is something only
1305 // ConfigureManager can access then only display when the user has
1306 // permissions ConfigureManager
1307 Privileges effectiveUserPrivileges =
1308 redfish::getUserPrivileges(req.userRole);
1309
1310 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1311 effectiveUserPrivileges))
1312 {
1313 asyncResp->res.jsonValue["LDAP"] = {
1314 {"Certificates",
1315 {{"@odata.id",
1316 "/redfish/v1/AccountService/LDAP/Certificates"}}}};
1317 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001318 crow::connections::systemBus->async_method_call(
1319 [asyncResp](
1320 const boost::system::error_code ec,
1321 const std::vector<
Ed Tanous168e20c2021-12-13 14:39:53 -08001322 std::pair<std::string, dbus::utility::DbusVariantType>>&
Ed Tanous6c51eab2021-06-03 12:30:29 -07001323 propertiesList) {
1324 if (ec)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001325 {
1326 messages::internalError(asyncResp->res);
1327 return;
1328 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001329 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1330 << "properties for AccountService";
1331 for (const std::pair<std::string,
Ed Tanous168e20c2021-12-13 14:39:53 -08001332 dbus::utility::DbusVariantType>&
1333 property : propertiesList)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001334 {
1335 if (property.first == "MinPasswordLength")
1336 {
1337 const uint8_t* value =
1338 std::get_if<uint8_t>(&property.second);
1339 if (value != nullptr)
Ratan Gupta24c85422019-01-30 19:41:24 +05301340 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001341 asyncResp->res.jsonValue["MinPasswordLength"] =
1342 *value;
Ratan Gupta24c85422019-01-30 19:41:24 +05301343 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001344 }
1345 if (property.first == "AccountUnlockTimeout")
1346 {
1347 const uint32_t* value =
1348 std::get_if<uint32_t>(&property.second);
1349 if (value != nullptr)
1350 {
1351 asyncResp->res
1352 .jsonValue["AccountLockoutDuration"] =
1353 *value;
1354 }
1355 }
1356 if (property.first == "MaxLoginAttemptBeforeLockout")
1357 {
1358 const uint16_t* value =
1359 std::get_if<uint16_t>(&property.second);
1360 if (value != nullptr)
1361 {
1362 asyncResp->res
1363 .jsonValue["AccountLockoutThreshold"] =
1364 *value;
1365 }
1366 }
1367 }
1368 },
1369 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1370 "org.freedesktop.DBus.Properties", "GetAll",
1371 "xyz.openbmc_project.User.AccountPolicy");
1372
1373 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
1374 const std::string& ldapType) {
1375 if (!success)
1376 {
1377 return;
1378 }
1379 parseLDAPConfigData(asyncResp->res.jsonValue, confData,
1380 ldapType);
1381 };
1382
1383 getLDAPConfigData("LDAP", callback);
1384 getLDAPConfigData("ActiveDirectory", callback);
1385 });
1386
Ed Tanousf5ffd802021-07-19 10:55:33 -07001387 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
1388 .privileges(redfish::privileges::getAccountService)
1389 .methods(boost::beast::http::verb::patch)(
1390 [](const crow::Request& req,
1391 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1392 std::optional<uint32_t> unlockTimeout;
1393 std::optional<uint16_t> lockoutThreshold;
1394 std::optional<uint16_t> minPasswordLength;
1395 std::optional<uint16_t> maxPasswordLength;
1396 std::optional<nlohmann::json> ldapObject;
1397 std::optional<nlohmann::json> activeDirectoryObject;
1398 std::optional<nlohmann::json> oemObject;
1399
1400 if (!json_util::readJson(
1401 req, asyncResp->res, "AccountLockoutDuration",
1402 unlockTimeout, "AccountLockoutThreshold",
1403 lockoutThreshold, "MaxPasswordLength",
1404 maxPasswordLength, "MinPasswordLength",
1405 minPasswordLength, "LDAP", ldapObject,
1406 "ActiveDirectory", activeDirectoryObject, "Oem",
1407 oemObject))
1408 {
1409 return;
1410 }
1411
1412 if (minPasswordLength)
1413 {
1414 messages::propertyNotWritable(asyncResp->res,
1415 "MinPasswordLength");
1416 }
1417
1418 if (maxPasswordLength)
1419 {
1420 messages::propertyNotWritable(asyncResp->res,
1421 "MaxPasswordLength");
1422 }
1423
1424 if (ldapObject)
1425 {
1426 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1427 }
1428
1429 if (std::optional<nlohmann::json> oemOpenBMCObject;
1430 oemObject &&
1431 json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1432 oemOpenBMCObject))
1433 {
1434 if (std::optional<nlohmann::json> authMethodsObject;
1435 oemOpenBMCObject &&
1436 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1437 "AuthMethods", authMethodsObject))
1438 {
1439 if (authMethodsObject)
1440 {
1441 handleAuthMethodsPatch(*authMethodsObject,
1442 asyncResp);
1443 }
1444 }
1445 }
1446
1447 if (activeDirectoryObject)
1448 {
1449 handleLDAPPatch(*activeDirectoryObject, asyncResp,
1450 "ActiveDirectory");
1451 }
1452
1453 if (unlockTimeout)
1454 {
1455 crow::connections::systemBus->async_method_call(
1456 [asyncResp](const boost::system::error_code ec) {
1457 if (ec)
1458 {
1459 messages::internalError(asyncResp->res);
1460 return;
1461 }
1462 messages::success(asyncResp->res);
1463 },
1464 "xyz.openbmc_project.User.Manager",
1465 "/xyz/openbmc_project/user",
1466 "org.freedesktop.DBus.Properties", "Set",
1467 "xyz.openbmc_project.User.AccountPolicy",
1468 "AccountUnlockTimeout",
Ed Tanous168e20c2021-12-13 14:39:53 -08001469 dbus::utility::DbusVariantType(*unlockTimeout));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001470 }
1471 if (lockoutThreshold)
1472 {
1473 crow::connections::systemBus->async_method_call(
1474 [asyncResp](const boost::system::error_code ec) {
1475 if (ec)
1476 {
1477 messages::internalError(asyncResp->res);
1478 return;
1479 }
1480 messages::success(asyncResp->res);
1481 },
1482 "xyz.openbmc_project.User.Manager",
1483 "/xyz/openbmc_project/user",
1484 "org.freedesktop.DBus.Properties", "Set",
1485 "xyz.openbmc_project.User.AccountPolicy",
1486 "MaxLoginAttemptBeforeLockout",
Ed Tanous168e20c2021-12-13 14:39:53 -08001487 dbus::utility::DbusVariantType(*lockoutThreshold));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001488 }
1489 });
1490
Ed Tanous6c51eab2021-06-03 12:30:29 -07001491 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001492 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001493 .methods(boost::beast::http::verb::get)(
1494 [](const crow::Request& req,
1495 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1496 asyncResp->res.jsonValue = {
1497 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
1498 {"@odata.type", "#ManagerAccountCollection."
1499 "ManagerAccountCollection"},
1500 {"Name", "Accounts Collection"},
1501 {"Description", "BMC User Accounts"}};
1502
Ed Tanous6c51eab2021-06-03 12:30:29 -07001503 Privileges effectiveUserPrivileges =
1504 redfish::getUserPrivileges(req.userRole);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001505
JunLin Chenf5e29f32021-12-08 16:47:04 +08001506 std::string thisUser;
1507 if (req.session)
1508 {
1509 thisUser = req.session->username;
1510 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001511 crow::connections::systemBus->async_method_call(
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001512 [asyncResp, thisUser, effectiveUserPrivileges](
1513 const boost::system::error_code ec,
1514 const ManagedObjectType& users) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001515 if (ec)
1516 {
1517 messages::internalError(asyncResp->res);
Ratan Gupta24c85422019-01-30 19:41:24 +05301518 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001519 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001520
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001521 bool userCanSeeAllAccounts =
1522 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001523 {"ConfigureUsers"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001524
1525 bool userCanSeeSelf =
1526 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001527 {"ConfigureSelf"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001528
Ed Tanous6c51eab2021-06-03 12:30:29 -07001529 nlohmann::json& memberArray =
1530 asyncResp->res.jsonValue["Members"];
1531 memberArray = nlohmann::json::array();
Ratan Gupta24c85422019-01-30 19:41:24 +05301532
Ed Tanous6c51eab2021-06-03 12:30:29 -07001533 for (auto& userpath : users)
1534 {
1535 std::string user = userpath.first.filename();
1536 if (user.empty())
Ratan Gupta24c85422019-01-30 19:41:24 +05301537 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301538 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001539 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1540
Ratan Gupta24c85422019-01-30 19:41:24 +05301541 return;
1542 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301543
Ed Tanous6c51eab2021-06-03 12:30:29 -07001544 // As clarified by Redfish here:
1545 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1546 // Users without ConfigureUsers, only see their own
1547 // account. Users with ConfigureUsers, see all
1548 // accounts.
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001549 if (userCanSeeAllAccounts ||
1550 (thisUser == user && userCanSeeSelf))
Ratan Gupta24c85422019-01-30 19:41:24 +05301551 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001552 memberArray.push_back(
1553 {{"@odata.id",
1554 "/redfish/v1/AccountService/Accounts/" +
1555 user}});
Ratan Gupta24c85422019-01-30 19:41:24 +05301556 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001557 }
1558 asyncResp->res.jsonValue["Members@odata.count"] =
1559 memberArray.size();
1560 },
1561 "xyz.openbmc_project.User.Manager",
1562 "/xyz/openbmc_project/user",
1563 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Ratan Gupta24c85422019-01-30 19:41:24 +05301564 });
Ed Tanous06e086d2018-09-19 17:19:52 -07001565
Ed Tanous6c51eab2021-06-03 12:30:29 -07001566 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001567 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001568 .methods(boost::beast::http::verb::post)([](const crow::Request& req,
1569 const std::shared_ptr<
1570 bmcweb::AsyncResp>&
1571 asyncResp) -> void {
1572 std::string username;
1573 std::string password;
1574 std::optional<std::string> roleId("User");
1575 std::optional<bool> enabled = true;
1576 if (!json_util::readJson(req, asyncResp->res, "UserName", username,
1577 "Password", password, "RoleId", roleId,
1578 "Enabled", enabled))
1579 {
1580 return;
1581 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001582
Ed Tanous6c51eab2021-06-03 12:30:29 -07001583 std::string priv = getPrivilegeFromRoleId(*roleId);
1584 if (priv.empty())
1585 {
1586 messages::propertyValueNotInList(asyncResp->res, *roleId,
1587 "RoleId");
1588 return;
1589 }
1590 // TODO: Following override will be reverted once support in
1591 // phosphor-user-manager is added. In order to avoid dependency
1592 // issues, this is added in bmcweb, which will removed, once
1593 // phosphor-user-manager supports priv-noaccess.
1594 if (priv == "priv-noaccess")
1595 {
1596 roleId = "";
1597 }
1598 else
1599 {
1600 roleId = priv;
1601 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001602
Ed Tanous6c51eab2021-06-03 12:30:29 -07001603 // Reading AllGroups property
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001604 sdbusplus::asio::getProperty<std::vector<std::string>>(
1605 *crow::connections::systemBus,
1606 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1607 "xyz.openbmc_project.User.Manager", "AllGroups",
Ed Tanous6c51eab2021-06-03 12:30:29 -07001608 [asyncResp, username, password{std::move(password)}, roleId,
Ed Tanous168e20c2021-12-13 14:39:53 -08001609 enabled](const boost::system::error_code ec,
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001610 const std::vector<std::string>& allGroupsList) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001611 if (ec)
1612 {
1613 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1614 messages::internalError(asyncResp->res);
1615 return;
1616 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001617
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001618 if (allGroupsList.empty())
Ed Tanous6c51eab2021-06-03 12:30:29 -07001619 {
1620 messages::internalError(asyncResp->res);
1621 return;
1622 }
1623
1624 crow::connections::systemBus->async_method_call(
1625 [asyncResp, username,
1626 password](const boost::system::error_code ec2,
1627 sdbusplus::message::message& m) {
1628 if (ec2)
1629 {
1630 userErrorMessageHandler(
1631 m.get_error(), asyncResp, username, "");
1632 return;
1633 }
1634
1635 if (pamUpdatePassword(username, password) !=
1636 PAM_SUCCESS)
1637 {
1638 // At this point we have a user that's been
1639 // created, but the password set
1640 // failed.Something is wrong, so delete the user
1641 // that we've already created
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301642 sdbusplus::message::object_path tempObjPath(
1643 rootUserDbusPath);
1644 tempObjPath /= username;
1645 const std::string userPath(tempObjPath);
1646
Ed Tanous6c51eab2021-06-03 12:30:29 -07001647 crow::connections::systemBus->async_method_call(
1648 [asyncResp, password](
1649 const boost::system::error_code ec3) {
1650 if (ec3)
1651 {
1652 messages::internalError(
1653 asyncResp->res);
1654 return;
1655 }
1656
1657 // If password is invalid
1658 messages::propertyValueFormatError(
1659 asyncResp->res, password,
1660 "Password");
1661 },
1662 "xyz.openbmc_project.User.Manager",
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301663 userPath,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001664 "xyz.openbmc_project.Object.Delete",
1665 "Delete");
1666
1667 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1668 return;
1669 }
1670
1671 messages::created(asyncResp->res);
1672 asyncResp->res.addHeader(
1673 "Location",
1674 "/redfish/v1/AccountService/Accounts/" +
1675 username);
1676 },
1677 "xyz.openbmc_project.User.Manager",
1678 "/xyz/openbmc_project/user",
1679 "xyz.openbmc_project.User.Manager", "CreateUser",
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001680 username, allGroupsList, *roleId, *enabled);
1681 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001682 });
1683
1684 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001685 .privileges(redfish::privileges::getManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001686 .methods(
1687 boost::beast::http::verb::
1688 get)([](const crow::Request& req,
1689 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1690 const std::string& accountName) -> void {
1691 if (req.session->username != accountName)
1692 {
1693 // At this point we've determined that the user is trying to
1694 // modify a user that isn't them. We need to verify that they
1695 // have permissions to modify other users, so re-run the auth
1696 // check with the same permissions, minus ConfigureSelf.
1697 Privileges effectiveUserPrivileges =
1698 redfish::getUserPrivileges(req.userRole);
1699 Privileges requiredPermissionsToChangeNonSelf = {
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001700 "ConfigureUsers", "ConfigureManager"};
Ed Tanous6c51eab2021-06-03 12:30:29 -07001701 if (!effectiveUserPrivileges.isSupersetOf(
1702 requiredPermissionsToChangeNonSelf))
Ed Tanous06e086d2018-09-19 17:19:52 -07001703 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001704 BMCWEB_LOG_DEBUG << "GET Account denied access";
1705 messages::insufficientPrivilege(asyncResp->res);
1706 return;
1707 }
1708 }
1709
1710 crow::connections::systemBus->async_method_call(
1711 [asyncResp, accountName](const boost::system::error_code ec,
1712 const ManagedObjectType& users) {
1713 if (ec)
1714 {
1715 messages::internalError(asyncResp->res);
1716 return;
1717 }
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301718 auto userIt = std::find_if(
1719 users.begin(), users.end(),
1720 [accountName](
1721 const std::pair<sdbusplus::message::object_path,
1722 DbusInterfaceType>& user) {
1723 return !accountName.compare(user.first.filename());
1724 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001725
Ed Tanous6c51eab2021-06-03 12:30:29 -07001726 if (userIt == users.end())
1727 {
1728 messages::resourceNotFound(
1729 asyncResp->res, "ManagerAccount", accountName);
1730 return;
1731 }
1732
1733 asyncResp->res.jsonValue = {
1734 {"@odata.type",
1735 "#ManagerAccount.v1_4_0.ManagerAccount"},
1736 {"Name", "User Account"},
1737 {"Description", "User Account"},
1738 {"Password", nullptr},
1739 {"AccountTypes", {"Redfish"}}};
1740
1741 for (const auto& interface : userIt->second)
1742 {
1743 if (interface.first ==
1744 "xyz.openbmc_project.User.Attributes")
1745 {
1746 for (const auto& property : interface.second)
1747 {
1748 if (property.first == "UserEnabled")
1749 {
1750 const bool* userEnabled =
1751 std::get_if<bool>(&property.second);
1752 if (userEnabled == nullptr)
1753 {
1754 BMCWEB_LOG_ERROR
1755 << "UserEnabled wasn't a bool";
1756 messages::internalError(asyncResp->res);
1757 return;
1758 }
1759 asyncResp->res.jsonValue["Enabled"] =
1760 *userEnabled;
1761 }
1762 else if (property.first ==
1763 "UserLockedForFailedAttempt")
1764 {
1765 const bool* userLocked =
1766 std::get_if<bool>(&property.second);
1767 if (userLocked == nullptr)
1768 {
1769 BMCWEB_LOG_ERROR << "UserLockedForF"
1770 "ailedAttempt "
1771 "wasn't a bool";
1772 messages::internalError(asyncResp->res);
1773 return;
1774 }
1775 asyncResp->res.jsonValue["Locked"] =
1776 *userLocked;
1777 asyncResp->res.jsonValue
1778 ["Locked@Redfish.AllowableValues"] = {
1779 "false"}; // can only unlock accounts
1780 }
1781 else if (property.first == "UserPrivilege")
1782 {
1783 const std::string* userPrivPtr =
1784 std::get_if<std::string>(
1785 &property.second);
1786 if (userPrivPtr == nullptr)
1787 {
1788 BMCWEB_LOG_ERROR
1789 << "UserPrivilege wasn't a "
1790 "string";
1791 messages::internalError(asyncResp->res);
1792 return;
1793 }
1794 std::string role =
1795 getRoleIdFromPrivilege(*userPrivPtr);
1796 if (role.empty())
1797 {
1798 BMCWEB_LOG_ERROR << "Invalid user role";
1799 messages::internalError(asyncResp->res);
1800 return;
1801 }
1802 asyncResp->res.jsonValue["RoleId"] = role;
1803
1804 asyncResp->res.jsonValue["Links"]["Role"] =
1805 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +08001806 "/redfish/v1/AccountService/Roles/" +
Ed Tanous6c51eab2021-06-03 12:30:29 -07001807 role}};
1808 }
1809 else if (property.first ==
1810 "UserPasswordExpired")
1811 {
1812 const bool* userPasswordExpired =
1813 std::get_if<bool>(&property.second);
1814 if (userPasswordExpired == nullptr)
1815 {
George Liu0fda0f12021-11-16 10:06:17 +08001816 BMCWEB_LOG_ERROR
1817 << "UserPasswordExpired wasn't a bool";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001818 messages::internalError(asyncResp->res);
1819 return;
1820 }
1821 asyncResp->res
1822 .jsonValue["PasswordChangeRequired"] =
1823 *userPasswordExpired;
1824 }
1825 }
1826 }
1827 }
1828
1829 asyncResp->res.jsonValue["@odata.id"] =
1830 "/redfish/v1/AccountService/Accounts/" + accountName;
1831 asyncResp->res.jsonValue["Id"] = accountName;
1832 asyncResp->res.jsonValue["UserName"] = accountName;
1833 },
1834 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1835 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1836 });
1837
1838 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001839 // TODO this privilege should be using the generated endpoints, but
1840 // because of the special handling of ConfigureSelf, it's not able to
1841 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001842 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1843 .methods(boost::beast::http::verb::patch)(
1844 [](const crow::Request& req,
1845 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1846 const std::string& username) -> void {
1847 std::optional<std::string> newUserName;
1848 std::optional<std::string> password;
1849 std::optional<bool> enabled;
1850 std::optional<std::string> roleId;
1851 std::optional<bool> locked;
Ed Tanouse9cc5172021-11-03 14:13:19 +08001852
1853 Privileges effectiveUserPrivileges =
1854 redfish::getUserPrivileges(req.userRole);
1855 Privileges configureUsers = {"ConfigureUsers"};
1856 bool userHasConfigureUsers =
1857 effectiveUserPrivileges.isSupersetOf(configureUsers);
1858 if (userHasConfigureUsers)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001859 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001860 // Users with ConfigureUsers can modify for all users
1861 if (!json_util::readJson(req, asyncResp->res, "UserName",
1862 newUserName, "Password", password,
1863 "RoleId", roleId, "Enabled",
1864 enabled, "Locked", locked))
1865 {
1866 return;
1867 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001868 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001869 else
Ed Tanous6c51eab2021-06-03 12:30:29 -07001870 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001871 // ConfigureSelf accounts can only modify their own account
1872 if (username != req.session->username)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001873 {
1874 messages::insufficientPrivilege(asyncResp->res);
1875 return;
1876 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001877 // ConfigureSelf accounts can only modify their password
1878 if (!json_util::readJson(req, asyncResp->res, "Password",
1879 password))
1880 {
1881 return;
1882 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001883 }
1884
1885 // if user name is not provided in the patch method or if it
1886 // matches the user name in the URI, then we are treating it as
1887 // updating user properties other then username. If username
1888 // provided doesn't match the URI, then we are treating this as
1889 // user rename request.
1890 if (!newUserName || (newUserName.value() == username))
1891 {
1892 updateUserProperties(asyncResp, username, password, enabled,
1893 roleId, locked);
1894 return;
1895 }
1896 crow::connections::systemBus->async_method_call(
1897 [asyncResp, username, password(std::move(password)),
1898 roleId(std::move(roleId)), enabled,
1899 newUser{std::string(*newUserName)},
1900 locked](const boost::system::error_code ec,
1901 sdbusplus::message::message& m) {
1902 if (ec)
1903 {
1904 userErrorMessageHandler(m.get_error(), asyncResp,
1905 newUser, username);
1906 return;
1907 }
1908
1909 updateUserProperties(asyncResp, newUser, password,
1910 enabled, roleId, locked);
1911 },
1912 "xyz.openbmc_project.User.Manager",
1913 "/xyz/openbmc_project/user",
1914 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1915 *newUserName);
1916 });
1917
1918 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001919 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001920 .methods(boost::beast::http::verb::delete_)(
1921 [](const crow::Request& /*req*/,
1922 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1923 const std::string& username) -> void {
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301924 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1925 tempObjPath /= username;
1926 const std::string userPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001927
1928 crow::connections::systemBus->async_method_call(
1929 [asyncResp, username](const boost::system::error_code ec) {
1930 if (ec)
1931 {
1932 messages::resourceNotFound(
1933 asyncResp->res,
1934 "#ManagerAccount.v1_4_0.ManagerAccount",
1935 username);
1936 return;
1937 }
1938
1939 messages::accountRemoved(asyncResp->res);
1940 },
1941 "xyz.openbmc_project.User.Manager", userPath,
1942 "xyz.openbmc_project.Object.Delete", "Delete");
1943 });
1944}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001945
Ed Tanous1abe55e2018-09-05 08:30:59 -07001946} // namespace redfish