blob: 53d644d5f2f48378c56ca0937377f6dc7d9b0965 [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 Tanous3174e4d2020-10-07 11:41:22 -070091 if ((role == "") || (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 Tanous3174e4d2020-10-07 11:41:22 -0700111 if ((role == "NoAccess") || (role == ""))
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 }
161
162 return;
163}
164
Ed Tanous81ce6092020-12-17 16:54:55 +0000165inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000166 const LDAPConfigData& confData,
167 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530168{
Ratan Guptaab828d72019-04-22 14:18:33 +0530169 std::string service =
170 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600171 nlohmann::json ldap = {
Ratan Gupta6973a582018-12-13 18:25:44 +0530172 {"ServiceEnabled", confData.serviceEnabled},
173 {"ServiceAddresses", nlohmann::json::array({confData.uri})},
174 {"Authentication",
175 {{"AuthenticationType", "UsernameAndPassword"},
Ratan Gupta6973a582018-12-13 18:25:44 +0530176 {"Username", confData.bindDN},
177 {"Password", nullptr}}},
178 {"LDAPService",
179 {{"SearchSettings",
180 {{"BaseDistinguishedNames",
181 nlohmann::json::array({confData.baseDN})},
182 {"UsernameAttribute", confData.userNameAttribute},
183 {"GroupsAttribute", confData.groupAttribute}}}}},
184 };
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600185
Ed Tanous81ce6092020-12-17 16:54:55 +0000186 jsonResponse[ldapType].update(ldap);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600187
Ed Tanous81ce6092020-12-17 16:54:55 +0000188 nlohmann::json& roleMapArray = jsonResponse[ldapType]["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600189 roleMapArray = nlohmann::json::array();
190 for (auto& obj : confData.groupRoleList)
191 {
192 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
193 << obj.second.groupName << "\n";
194 roleMapArray.push_back(
195 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
196 nlohmann::json::array(
197 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
198 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530199}
200
201/**
Ratan Gupta06785242019-07-26 22:30:16 +0530202 * @brief validates given JSON input and then calls appropriate method to
203 * create, to delete or to set Rolemapping object based on the given input.
204 *
205 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000206inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800207 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530208 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700209 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530210{
211 for (size_t index = 0; index < input.size(); index++)
212 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700213 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530214
215 if (thisJson.is_null())
216 {
217 // delete the existing object
218 if (index < roleMapObjData.size())
219 {
220 crow::connections::systemBus->async_method_call(
221 [asyncResp, roleMapObjData, serverType,
222 index](const boost::system::error_code ec) {
223 if (ec)
224 {
225 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
226 messages::internalError(asyncResp->res);
227 return;
228 }
229 asyncResp->res
230 .jsonValue[serverType]["RemoteRoleMapping"][index] =
231 nullptr;
232 },
233 ldapDbusService, roleMapObjData[index].first,
234 "xyz.openbmc_project.Object.Delete", "Delete");
235 }
236 else
237 {
238 BMCWEB_LOG_ERROR << "Can't delete the object";
239 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800240 asyncResp->res,
241 thisJson.dump(2, ' ', true,
242 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530243 "RemoteRoleMapping/" + std::to_string(index));
244 return;
245 }
246 }
247 else if (thisJson.empty())
248 {
249 // Don't do anything for the empty objects,parse next json
250 // eg {"RemoteRoleMapping",[{}]}
251 }
252 else
253 {
254 // update/create the object
255 std::optional<std::string> remoteGroup;
256 std::optional<std::string> localRole;
257
Ed Tanousf23b7292020-10-15 09:41:17 -0700258 // This is a copy, but it's required in this case because of how
259 // readJson is structured
260 nlohmann::json thisJsonCopy = thisJson;
261 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
262 "RemoteGroup", remoteGroup, "LocalRole",
263 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530264 {
265 continue;
266 }
267
268 // Update existing RoleMapping Object
269 if (index < roleMapObjData.size())
270 {
271 BMCWEB_LOG_DEBUG << "Update Role Map Object";
272 // If "RemoteGroup" info is provided
273 if (remoteGroup)
274 {
275 crow::connections::systemBus->async_method_call(
276 [asyncResp, roleMapObjData, serverType, index,
277 remoteGroup](const boost::system::error_code ec) {
278 if (ec)
279 {
280 BMCWEB_LOG_ERROR << "DBUS response error: "
281 << ec;
282 messages::internalError(asyncResp->res);
283 return;
284 }
285 asyncResp->res
286 .jsonValue[serverType]["RemoteRoleMapping"]
287 [index]["RemoteGroup"] = *remoteGroup;
288 },
289 ldapDbusService, roleMapObjData[index].first,
290 propertyInterface, "Set",
291 "xyz.openbmc_project.User.PrivilegeMapperEntry",
292 "GroupName",
Ed Tanous168e20c2021-12-13 14:39:53 -0800293 dbus::utility::DbusVariantType(
294 std::move(*remoteGroup)));
Ratan Gupta06785242019-07-26 22:30:16 +0530295 }
296
297 // If "LocalRole" info is provided
298 if (localRole)
299 {
300 crow::connections::systemBus->async_method_call(
301 [asyncResp, roleMapObjData, serverType, index,
302 localRole](const boost::system::error_code ec) {
303 if (ec)
304 {
305 BMCWEB_LOG_ERROR << "DBUS response error: "
306 << ec;
307 messages::internalError(asyncResp->res);
308 return;
309 }
310 asyncResp->res
311 .jsonValue[serverType]["RemoteRoleMapping"]
312 [index]["LocalRole"] = *localRole;
313 },
314 ldapDbusService, roleMapObjData[index].first,
315 propertyInterface, "Set",
316 "xyz.openbmc_project.User.PrivilegeMapperEntry",
317 "Privilege",
Ed Tanous168e20c2021-12-13 14:39:53 -0800318 dbus::utility::DbusVariantType(
Ratan Gupta06785242019-07-26 22:30:16 +0530319 getPrivilegeFromRoleId(std::move(*localRole))));
320 }
321 }
322 // Create a new RoleMapping Object.
323 else
324 {
325 BMCWEB_LOG_DEBUG
326 << "setRoleMappingProperties: Creating new Object";
327 std::string pathString =
328 "RemoteRoleMapping/" + std::to_string(index);
329
330 if (!localRole)
331 {
332 messages::propertyMissing(asyncResp->res,
333 pathString + "/LocalRole");
334 continue;
335 }
336 if (!remoteGroup)
337 {
338 messages::propertyMissing(asyncResp->res,
339 pathString + "/RemoteGroup");
340 continue;
341 }
342
343 std::string dbusObjectPath;
344 if (serverType == "ActiveDirectory")
345 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700346 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530347 }
348 else if (serverType == "LDAP")
349 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000350 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530351 }
352
353 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
354 << ",LocalRole=" << *localRole;
355
356 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700357 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530358 remoteGroup](const boost::system::error_code ec) {
359 if (ec)
360 {
361 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
362 messages::internalError(asyncResp->res);
363 return;
364 }
365 nlohmann::json& remoteRoleJson =
366 asyncResp->res
367 .jsonValue[serverType]["RemoteRoleMapping"];
368 remoteRoleJson.push_back(
369 {{"LocalRole", *localRole},
370 {"RemoteGroup", *remoteGroup}});
371 },
372 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700373 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530374 getPrivilegeFromRoleId(std::move(*localRole)));
375 }
376 }
377 }
378}
379
380/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530381 * Function that retrieves all properties for LDAP config object
382 * into JSON
383 */
384template <typename CallbackFunc>
385inline void getLDAPConfigData(const std::string& ldapType,
386 CallbackFunc&& callback)
387{
Ratan Guptaab828d72019-04-22 14:18:33 +0530388
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600389 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530390 ldapConfigInterface};
391
392 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600393 [callback, ldapType](const boost::system::error_code ec,
394 const GetObjectType& resp) {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600395 if (ec || resp.empty())
396 {
George Liu0fda0f12021-11-16 10:06:17 +0800397 BMCWEB_LOG_ERROR
398 << "DBUS response error during getting of service name: "
399 << ec;
Ed Tanous23a21a12020-07-25 04:45:05 +0000400 LDAPConfigData empty{};
401 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600402 return;
403 }
404 std::string service = resp.begin()->first;
405 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800406 [callback, ldapType](
407 const boost::system::error_code errorCode,
408 const dbus::utility::ManagedObjectType& ldapObjects) {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600409 LDAPConfigData confData{};
Ed Tanous81ce6092020-12-17 16:54:55 +0000410 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600411 {
412 callback(false, confData, ldapType);
413 BMCWEB_LOG_ERROR << "D-Bus responses error: "
Ed Tanous81ce6092020-12-17 16:54:55 +0000414 << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600415 return;
416 }
417
418 std::string ldapDbusType;
419 std::string searchString;
420
421 if (ldapType == "LDAP")
422 {
George Liu0fda0f12021-11-16 10:06:17 +0800423 ldapDbusType =
424 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600425 searchString = "openldap";
426 }
427 else if (ldapType == "ActiveDirectory")
428 {
429 ldapDbusType =
George Liu0fda0f12021-11-16 10:06:17 +0800430 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600431 searchString = "active_directory";
432 }
433 else
434 {
435 BMCWEB_LOG_ERROR
436 << "Can't get the DbusType for the given type="
437 << ldapType;
438 callback(false, confData, ldapType);
439 return;
440 }
441
442 std::string ldapEnableInterfaceStr = ldapEnableInterface;
443 std::string ldapConfigInterfaceStr = ldapConfigInterface;
444
445 for (const auto& object : ldapObjects)
446 {
447 // let's find the object whose ldap type is equal to the
448 // given type
449 if (object.first.str.find(searchString) ==
450 std::string::npos)
451 {
452 continue;
453 }
454
455 for (const auto& interface : object.second)
456 {
457 if (interface.first == ldapEnableInterfaceStr)
458 {
459 // rest of the properties are string.
460 for (const auto& property : interface.second)
461 {
462 if (property.first == "Enabled")
463 {
464 const bool* value =
465 std::get_if<bool>(&property.second);
466 if (value == nullptr)
467 {
468 continue;
469 }
470 confData.serviceEnabled = *value;
471 break;
472 }
473 }
474 }
475 else if (interface.first == ldapConfigInterfaceStr)
476 {
477
478 for (const auto& property : interface.second)
479 {
Ed Tanous271584a2019-07-09 16:24:22 -0700480 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600481 std::get_if<std::string>(
482 &property.second);
Ed Tanous271584a2019-07-09 16:24:22 -0700483 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600484 {
485 continue;
486 }
487 if (property.first == "LDAPServerURI")
488 {
Ed Tanous271584a2019-07-09 16:24:22 -0700489 confData.uri = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600490 }
491 else if (property.first == "LDAPBindDN")
492 {
Ed Tanous271584a2019-07-09 16:24:22 -0700493 confData.bindDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600494 }
495 else if (property.first == "LDAPBaseDN")
496 {
Ed Tanous271584a2019-07-09 16:24:22 -0700497 confData.baseDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600498 }
499 else if (property.first ==
500 "LDAPSearchScope")
501 {
Ed Tanous271584a2019-07-09 16:24:22 -0700502 confData.searchScope = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600503 }
504 else if (property.first ==
505 "GroupNameAttribute")
506 {
Ed Tanous271584a2019-07-09 16:24:22 -0700507 confData.groupAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600508 }
509 else if (property.first ==
510 "UserNameAttribute")
511 {
Ed Tanous271584a2019-07-09 16:24:22 -0700512 confData.userNameAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600513 }
514 else if (property.first == "LDAPType")
515 {
Ed Tanous271584a2019-07-09 16:24:22 -0700516 confData.serverType = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600517 }
518 }
519 }
George Liu0fda0f12021-11-16 10:06:17 +0800520 else if (
521 interface.first ==
522 "xyz.openbmc_project.User.PrivilegeMapperEntry")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600523 {
524 LDAPRoleMapData roleMapData{};
525 for (const auto& property : interface.second)
526 {
Ed Tanous271584a2019-07-09 16:24:22 -0700527 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600528 std::get_if<std::string>(
529 &property.second);
530
Ed Tanous271584a2019-07-09 16:24:22 -0700531 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600532 {
533 continue;
534 }
535
536 if (property.first == "GroupName")
537 {
Ed Tanous271584a2019-07-09 16:24:22 -0700538 roleMapData.groupName = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600539 }
540 else if (property.first == "Privilege")
541 {
Ed Tanous271584a2019-07-09 16:24:22 -0700542 roleMapData.privilege = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600543 }
544 }
545
Ed Tanous0f0353b2019-10-24 11:37:51 -0700546 confData.groupRoleList.emplace_back(
547 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600548 }
549 }
550 }
551 callback(true, confData, ldapType);
552 },
553 service, ldapRootObject, dbusObjManagerIntf,
554 "GetManagedObjects");
555 },
556 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
Ed Tanous23a21a12020-07-25 04:45:05 +0000557 ldapConfigObjectName, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530558}
559
Ed Tanous6c51eab2021-06-03 12:30:29 -0700560/**
561 * @brief parses the authentication section under the LDAP
562 * @param input JSON data
563 * @param asyncResp pointer to the JSON response
564 * @param userName userName to be filled from the given JSON.
565 * @param password password to be filled from the given JSON.
566 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700567inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700568 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
569 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700570{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700571 std::optional<std::string> authType;
572
573 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
574 authType, "Username", username, "Password",
575 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700577 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700579 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530580 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700581 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530582 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700583 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530584 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700585 messages::propertyValueNotInList(asyncResp->res, *authType,
586 "AuthenticationType");
587 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530588 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700589}
590/**
591 * @brief parses the LDAPService section under the LDAP
592 * @param input JSON data
593 * @param asyncResp pointer to the JSON response
594 * @param baseDNList baseDN to be filled from the given JSON.
595 * @param userNameAttribute userName to be filled from the given JSON.
596 * @param groupaAttribute password to be filled from the given JSON.
597 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530598
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700599inline void
600 parseLDAPServiceJson(nlohmann::json input,
601 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
602 std::optional<std::vector<std::string>>& baseDNList,
603 std::optional<std::string>& userNameAttribute,
604 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700605{
606 std::optional<nlohmann::json> searchSettings;
607
608 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
609 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530610 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700611 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530612 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700613 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530614 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700615 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530616 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700617 if (!json_util::readJson(*searchSettings, asyncResp->res,
618 "BaseDistinguishedNames", baseDNList,
619 "UsernameAttribute", userNameAttribute,
620 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530621 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700622 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530623 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700624}
625/**
626 * @brief updates the LDAP server address and updates the
627 json response with the new value.
628 * @param serviceAddressList address to be updated.
629 * @param asyncResp pointer to the JSON response
630 * @param ldapServerElementName Type of LDAP
631 server(openLDAP/ActiveDirectory)
632 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530633
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700634inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700635 const std::vector<std::string>& serviceAddressList,
636 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
637 const std::string& ldapServerElementName,
638 const std::string& ldapConfigObject)
639{
640 crow::connections::systemBus->async_method_call(
641 [asyncResp, ldapServerElementName,
642 serviceAddressList](const boost::system::error_code ec) {
643 if (ec)
644 {
645 BMCWEB_LOG_DEBUG
646 << "Error Occurred in updating the service address";
647 messages::internalError(asyncResp->res);
648 return;
649 }
650 std::vector<std::string> modifiedserviceAddressList = {
651 serviceAddressList.front()};
652 asyncResp->res
653 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
654 modifiedserviceAddressList;
655 if ((serviceAddressList).size() > 1)
656 {
657 messages::propertyValueModified(asyncResp->res,
658 "ServiceAddresses",
659 serviceAddressList.front());
660 }
661 BMCWEB_LOG_DEBUG << "Updated the service address";
662 },
663 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
664 ldapConfigInterface, "LDAPServerURI",
Ed Tanous168e20c2021-12-13 14:39:53 -0800665 dbus::utility::DbusVariantType(serviceAddressList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700666}
667/**
668 * @brief updates the LDAP Bind DN and updates the
669 json response with the new value.
670 * @param username name of the user which needs to be updated.
671 * @param asyncResp pointer to the JSON response
672 * @param ldapServerElementName Type of LDAP
673 server(openLDAP/ActiveDirectory)
674 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530675
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700676inline void
677 handleUserNamePatch(const std::string& username,
678 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
679 const std::string& ldapServerElementName,
680 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700681{
682 crow::connections::systemBus->async_method_call(
683 [asyncResp, username,
684 ldapServerElementName](const boost::system::error_code ec) {
685 if (ec)
686 {
687 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
688 messages::internalError(asyncResp->res);
689 return;
690 }
691 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
692 ["Username"] = username;
693 BMCWEB_LOG_DEBUG << "Updated the username";
694 },
695 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800696 ldapConfigInterface, "LDAPBindDN",
697 dbus::utility::DbusVariantType(username));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700698}
699
700/**
701 * @brief updates the LDAP password
702 * @param password : ldap password which needs to be updated.
703 * @param asyncResp pointer to the JSON response
704 * @param ldapServerElementName Type of LDAP
705 * server(openLDAP/ActiveDirectory)
706 */
707
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700708inline void
709 handlePasswordPatch(const std::string& password,
710 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
711 const std::string& ldapServerElementName,
712 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700713{
714 crow::connections::systemBus->async_method_call(
715 [asyncResp, password,
716 ldapServerElementName](const boost::system::error_code ec) {
717 if (ec)
718 {
719 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
720 messages::internalError(asyncResp->res);
721 return;
722 }
723 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
724 ["Password"] = "";
725 BMCWEB_LOG_DEBUG << "Updated the password";
726 },
727 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
728 ldapConfigInterface, "LDAPBindDNPassword",
Ed Tanous168e20c2021-12-13 14:39:53 -0800729 dbus::utility::DbusVariantType(password));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700730}
731
732/**
733 * @brief updates the LDAP BaseDN and updates the
734 json response with the new value.
735 * @param baseDNList baseDN list which needs to be updated.
736 * @param asyncResp pointer to the JSON response
737 * @param ldapServerElementName Type of LDAP
738 server(openLDAP/ActiveDirectory)
739 */
740
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700741inline void
742 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
743 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
744 const std::string& ldapServerElementName,
745 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700746{
747 crow::connections::systemBus->async_method_call(
748 [asyncResp, baseDNList,
749 ldapServerElementName](const boost::system::error_code ec) {
750 if (ec)
751 {
752 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
753 messages::internalError(asyncResp->res);
754 return;
755 }
756 auto& serverTypeJson =
757 asyncResp->res.jsonValue[ldapServerElementName];
758 auto& searchSettingsJson =
759 serverTypeJson["LDAPService"]["SearchSettings"];
760 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
761 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
762 if (baseDNList.size() > 1)
763 {
764 messages::propertyValueModified(asyncResp->res,
765 "BaseDistinguishedNames",
766 baseDNList.front());
767 }
768 BMCWEB_LOG_DEBUG << "Updated the base DN";
769 },
770 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
771 ldapConfigInterface, "LDAPBaseDN",
Ed Tanous168e20c2021-12-13 14:39:53 -0800772 dbus::utility::DbusVariantType(baseDNList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700773}
774/**
775 * @brief updates the LDAP user name attribute and updates the
776 json response with the new value.
777 * @param userNameAttribute attribute to be updated.
778 * @param asyncResp pointer to the JSON response
779 * @param ldapServerElementName Type of LDAP
780 server(openLDAP/ActiveDirectory)
781 */
782
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700783inline void
784 handleUserNameAttrPatch(const std::string& userNameAttribute,
785 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
786 const std::string& ldapServerElementName,
787 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700788{
789 crow::connections::systemBus->async_method_call(
790 [asyncResp, userNameAttribute,
791 ldapServerElementName](const boost::system::error_code ec) {
792 if (ec)
793 {
794 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
795 "username attribute";
796 messages::internalError(asyncResp->res);
797 return;
798 }
799 auto& serverTypeJson =
800 asyncResp->res.jsonValue[ldapServerElementName];
801 auto& searchSettingsJson =
802 serverTypeJson["LDAPService"]["SearchSettings"];
803 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
804 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
805 },
806 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
807 ldapConfigInterface, "UserNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800808 dbus::utility::DbusVariantType(userNameAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700809}
810/**
811 * @brief updates the LDAP group attribute and updates the
812 json response with the new value.
813 * @param groupsAttribute attribute to be updated.
814 * @param asyncResp pointer to the JSON response
815 * @param ldapServerElementName Type of LDAP
816 server(openLDAP/ActiveDirectory)
817 */
818
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700819inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700820 const std::string& groupsAttribute,
821 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
822 const std::string& ldapServerElementName,
823 const std::string& ldapConfigObject)
824{
825 crow::connections::systemBus->async_method_call(
826 [asyncResp, groupsAttribute,
827 ldapServerElementName](const boost::system::error_code ec) {
828 if (ec)
829 {
830 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
831 "groupname attribute";
832 messages::internalError(asyncResp->res);
833 return;
834 }
835 auto& serverTypeJson =
836 asyncResp->res.jsonValue[ldapServerElementName];
837 auto& searchSettingsJson =
838 serverTypeJson["LDAPService"]["SearchSettings"];
839 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
840 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
841 },
842 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
843 ldapConfigInterface, "GroupNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800844 dbus::utility::DbusVariantType(groupsAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700845}
846/**
847 * @brief updates the LDAP service enable and updates the
848 json response with the new value.
849 * @param input JSON data.
850 * @param asyncResp pointer to the JSON response
851 * @param ldapServerElementName Type of LDAP
852 server(openLDAP/ActiveDirectory)
853 */
854
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700855inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700856 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
857 const std::string& ldapServerElementName,
858 const std::string& ldapConfigObject)
859{
860 crow::connections::systemBus->async_method_call(
861 [asyncResp, serviceEnabled,
862 ldapServerElementName](const boost::system::error_code ec) {
863 if (ec)
864 {
865 BMCWEB_LOG_DEBUG
866 << "Error Occurred in Updating the service enable";
867 messages::internalError(asyncResp->res);
868 return;
869 }
870 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
871 serviceEnabled;
872 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
873 },
874 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800875 ldapEnableInterface, "Enabled",
876 dbus::utility::DbusVariantType(serviceEnabled));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700877}
878
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700879inline void
880 handleAuthMethodsPatch(nlohmann::json& input,
881 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700882{
883 std::optional<bool> basicAuth;
884 std::optional<bool> cookie;
885 std::optional<bool> sessionToken;
886 std::optional<bool> xToken;
887 std::optional<bool> tls;
888
889 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
890 "Cookie", cookie, "SessionToken", sessionToken,
891 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530892 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700893 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
894 return;
895 }
896
897 // Make a copy of methods configuration
898 persistent_data::AuthConfigMethods authMethodsConfig =
899 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
900
901 if (basicAuth)
902 {
903#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
904 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800905 asyncResp->res,
906 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700907 return;
908#endif
909 authMethodsConfig.basic = *basicAuth;
910 }
911
912 if (cookie)
913 {
914#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800915 messages::actionNotSupported(
916 asyncResp->res,
917 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700918 return;
919#endif
920 authMethodsConfig.cookie = *cookie;
921 }
922
923 if (sessionToken)
924 {
925#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
926 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800927 asyncResp->res,
928 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700929 return;
930#endif
931 authMethodsConfig.sessionToken = *sessionToken;
932 }
933
934 if (xToken)
935 {
936#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800937 messages::actionNotSupported(
938 asyncResp->res,
939 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700940 return;
941#endif
942 authMethodsConfig.xtoken = *xToken;
943 }
944
945 if (tls)
946 {
947#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800948 messages::actionNotSupported(
949 asyncResp->res,
950 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700951 return;
952#endif
953 authMethodsConfig.tls = *tls;
954 }
955
956 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
957 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
958 !authMethodsConfig.tls)
959 {
960 // Do not allow user to disable everything
961 messages::actionNotSupported(asyncResp->res,
962 "of disabling all available methods");
963 return;
964 }
965
966 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
967 authMethodsConfig);
968 // Save configuration immediately
969 persistent_data::getConfig().writeData();
970
971 messages::success(asyncResp->res);
972}
973
974/**
975 * @brief Get the required values from the given JSON, validates the
976 * value and create the LDAP config object.
977 * @param input JSON data
978 * @param asyncResp pointer to the JSON response
979 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
980 */
981
982inline void handleLDAPPatch(nlohmann::json& input,
983 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
984 const std::string& serverType)
985{
986 std::string dbusObjectPath;
987 if (serverType == "ActiveDirectory")
988 {
989 dbusObjectPath = adConfigObject;
990 }
991 else if (serverType == "LDAP")
992 {
993 dbusObjectPath = ldapConfigObjectName;
994 }
995 else
996 {
997 return;
998 }
999
1000 std::optional<nlohmann::json> authentication;
1001 std::optional<nlohmann::json> ldapService;
1002 std::optional<std::vector<std::string>> serviceAddressList;
1003 std::optional<bool> serviceEnabled;
1004 std::optional<std::vector<std::string>> baseDNList;
1005 std::optional<std::string> userNameAttribute;
1006 std::optional<std::string> groupsAttribute;
1007 std::optional<std::string> userName;
1008 std::optional<std::string> password;
1009 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1010
1011 if (!json_util::readJson(input, asyncResp->res, "Authentication",
1012 authentication, "LDAPService", ldapService,
1013 "ServiceAddresses", serviceAddressList,
1014 "ServiceEnabled", serviceEnabled,
1015 "RemoteRoleMapping", remoteRoleMapData))
1016 {
1017 return;
1018 }
1019
1020 if (authentication)
1021 {
1022 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1023 password);
1024 }
1025 if (ldapService)
1026 {
1027 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1028 userNameAttribute, groupsAttribute);
1029 }
1030 if (serviceAddressList)
1031 {
1032 if ((*serviceAddressList).size() == 0)
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301033 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001034 messages::propertyValueNotInList(asyncResp->res, "[]",
1035 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001036 return;
1037 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001038 }
1039 if (baseDNList)
1040 {
1041 if ((*baseDNList).size() == 0)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301042 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001043 messages::propertyValueNotInList(asyncResp->res, "[]",
1044 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301045 return;
1046 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001047 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301048
Ed Tanous6c51eab2021-06-03 12:30:29 -07001049 // nothing to update, then return
1050 if (!userName && !password && !serviceAddressList && !baseDNList &&
1051 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1052 !remoteRoleMapData)
1053 {
1054 return;
1055 }
1056
1057 // Get the existing resource first then keep modifying
1058 // whenever any property gets updated.
1059 getLDAPConfigData(serverType, [asyncResp, userName, password, baseDNList,
1060 userNameAttribute, groupsAttribute,
1061 serviceAddressList, serviceEnabled,
1062 dbusObjectPath, remoteRoleMapData](
1063 bool success,
1064 const LDAPConfigData& confData,
1065 const std::string& serverT) {
1066 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301067 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001068 messages::internalError(asyncResp->res);
1069 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301070 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001071 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1072 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301073 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001074 // Disable the service first and update the rest of
1075 // the properties.
1076 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301077 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001078
Ratan Gupta8a07d282019-03-16 08:33:47 +05301079 if (serviceAddressList)
1080 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001081 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1082 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301083 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001084 if (userName)
1085 {
1086 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1087 }
1088 if (password)
1089 {
1090 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1091 }
1092
Ratan Gupta8a07d282019-03-16 08:33:47 +05301093 if (baseDNList)
1094 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001095 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1096 }
1097 if (userNameAttribute)
1098 {
1099 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1100 dbusObjectPath);
1101 }
1102 if (groupsAttribute)
1103 {
1104 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1105 dbusObjectPath);
1106 }
1107 if (serviceEnabled)
1108 {
1109 // if user has given the value as true then enable
1110 // the service. if user has given false then no-op
1111 // as service is already stopped.
1112 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301113 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001114 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1115 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301116 }
1117 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001118 else
1119 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001120 // if user has not given the service enabled value
1121 // then revert it to the same state as it was
1122 // before.
1123 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1124 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001125 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001126
Ed Tanous6c51eab2021-06-03 12:30:29 -07001127 if (remoteRoleMapData)
1128 {
1129 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1130 *remoteRoleMapData);
1131 }
1132 });
1133}
1134
1135inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1136 const std::string& username,
1137 std::optional<std::string> password,
1138 std::optional<bool> enabled,
1139 std::optional<std::string> roleId,
1140 std::optional<bool> locked)
1141{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301142 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1143 tempObjPath /= username;
1144 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001145
1146 dbus::utility::checkDbusPathExists(
1147 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001148 [dbusObjectPath, username, password(std::move(password)),
1149 roleId(std::move(roleId)), enabled, locked,
1150 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001151 if (!rc)
1152 {
1153 messages::resourceNotFound(
1154 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1155 username);
1156 return;
1157 }
1158
1159 if (password)
1160 {
1161 int retval = pamUpdatePassword(username, *password);
1162
1163 if (retval == PAM_USER_UNKNOWN)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001164 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001165 messages::resourceNotFound(
1166 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1167 username);
1168 }
1169 else if (retval == PAM_AUTHTOK_ERR)
1170 {
1171 // If password is invalid
1172 messages::propertyValueFormatError(asyncResp->res,
1173 *password, "Password");
1174 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1175 }
1176 else if (retval != PAM_SUCCESS)
1177 {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001178 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001179 return;
1180 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001181 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001182
Ed Tanous6c51eab2021-06-03 12:30:29 -07001183 if (enabled)
1184 {
1185 crow::connections::systemBus->async_method_call(
1186 [asyncResp](const boost::system::error_code ec) {
1187 if (ec)
1188 {
1189 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1190 messages::internalError(asyncResp->res);
1191 return;
1192 }
1193 messages::success(asyncResp->res);
1194 return;
1195 },
1196 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1197 "org.freedesktop.DBus.Properties", "Set",
1198 "xyz.openbmc_project.User.Attributes", "UserEnabled",
Ed Tanous168e20c2021-12-13 14:39:53 -08001199 dbus::utility::DbusVariantType{*enabled});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001200 }
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001201
Ed Tanous6c51eab2021-06-03 12:30:29 -07001202 if (roleId)
1203 {
1204 std::string priv = getPrivilegeFromRoleId(*roleId);
1205 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001206 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001207 messages::propertyValueNotInList(asyncResp->res, *roleId,
1208 "RoleId");
1209 return;
1210 }
1211 if (priv == "priv-noaccess")
1212 {
1213 priv = "";
1214 }
1215
1216 crow::connections::systemBus->async_method_call(
1217 [asyncResp](const boost::system::error_code ec) {
1218 if (ec)
1219 {
1220 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1221 messages::internalError(asyncResp->res);
1222 return;
1223 }
1224 messages::success(asyncResp->res);
1225 },
1226 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1227 "org.freedesktop.DBus.Properties", "Set",
1228 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
Ed Tanous168e20c2021-12-13 14:39:53 -08001229 dbus::utility::DbusVariantType{priv});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001230 }
1231
1232 if (locked)
1233 {
1234 // admin can unlock the account which is locked by
1235 // successive authentication failures but admin should
1236 // not be allowed to lock an account.
1237 if (*locked)
1238 {
1239 messages::propertyValueNotInList(asyncResp->res, "true",
1240 "Locked");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001241 return;
1242 }
1243
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001244 crow::connections::systemBus->async_method_call(
Ed Tanous6c51eab2021-06-03 12:30:29 -07001245 [asyncResp](const boost::system::error_code ec) {
1246 if (ec)
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001247 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001248 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1249 messages::internalError(asyncResp->res);
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001250 return;
1251 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001252 messages::success(asyncResp->res);
1253 return;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001254 },
Ed Tanous6c51eab2021-06-03 12:30:29 -07001255 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1256 "org.freedesktop.DBus.Properties", "Set",
1257 "xyz.openbmc_project.User.Attributes",
Ed Tanous168e20c2021-12-13 14:39:53 -08001258 "UserLockedForFailedAttempt",
1259 dbus::utility::DbusVariantType{*locked});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001260 }
1261 });
1262}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001263
Ed Tanous6c51eab2021-06-03 12:30:29 -07001264inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001265{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001266
Ed Tanous6c51eab2021-06-03 12:30:29 -07001267 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001268 .privileges(redfish::privileges::getAccountService)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001269 .methods(
Abhishek Patel72048782021-06-02 09:53:24 -05001270 boost::beast::http::verb::get)([](const crow::Request& req,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001271 const std::shared_ptr<
1272 bmcweb::AsyncResp>& asyncResp)
1273 -> void {
1274 const persistent_data::AuthConfigMethods& authMethodsConfig =
1275 persistent_data::SessionStore::getInstance()
1276 .getAuthMethodsConfig();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001277
Ed Tanous6c51eab2021-06-03 12:30:29 -07001278 asyncResp->res.jsonValue = {
1279 {"@odata.id", "/redfish/v1/AccountService"},
1280 {"@odata.type", "#AccountService."
1281 "v1_5_0.AccountService"},
1282 {"Id", "AccountService"},
1283 {"Name", "Account Service"},
1284 {"Description", "Account Service"},
1285 {"ServiceEnabled", true},
1286 {"MaxPasswordLength", 20},
1287 {"Accounts",
1288 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
1289 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
1290 {"Oem",
1291 {{"OpenBMC",
1292 {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
Ed Tanousd8e3c292021-09-21 18:01:43 -07001293 {"@odata.id", "/redfish/v1/AccountService#/Oem/OpenBMC"},
Ed Tanous6c51eab2021-06-03 12:30:29 -07001294 {"AuthMethods",
1295 {
1296 {"BasicAuth", authMethodsConfig.basic},
1297 {"SessionToken", authMethodsConfig.sessionToken},
1298 {"XToken", authMethodsConfig.xtoken},
1299 {"Cookie", authMethodsConfig.cookie},
1300 {"TLS", authMethodsConfig.tls},
Abhishek Patel72048782021-06-02 09:53:24 -05001301 }}}}}}};
1302 // /redfish/v1/AccountService/LDAP/Certificates is something only
1303 // ConfigureManager can access then only display when the user has
1304 // permissions ConfigureManager
1305 Privileges effectiveUserPrivileges =
1306 redfish::getUserPrivileges(req.userRole);
1307
1308 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1309 effectiveUserPrivileges))
1310 {
1311 asyncResp->res.jsonValue["LDAP"] = {
1312 {"Certificates",
1313 {{"@odata.id",
1314 "/redfish/v1/AccountService/LDAP/Certificates"}}}};
1315 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001316 crow::connections::systemBus->async_method_call(
1317 [asyncResp](
1318 const boost::system::error_code ec,
1319 const std::vector<
Ed Tanous168e20c2021-12-13 14:39:53 -08001320 std::pair<std::string, dbus::utility::DbusVariantType>>&
Ed Tanous6c51eab2021-06-03 12:30:29 -07001321 propertiesList) {
1322 if (ec)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001323 {
1324 messages::internalError(asyncResp->res);
1325 return;
1326 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001327 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1328 << "properties for AccountService";
1329 for (const std::pair<std::string,
Ed Tanous168e20c2021-12-13 14:39:53 -08001330 dbus::utility::DbusVariantType>&
1331 property : propertiesList)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001332 {
1333 if (property.first == "MinPasswordLength")
1334 {
1335 const uint8_t* value =
1336 std::get_if<uint8_t>(&property.second);
1337 if (value != nullptr)
Ratan Gupta24c85422019-01-30 19:41:24 +05301338 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001339 asyncResp->res.jsonValue["MinPasswordLength"] =
1340 *value;
Ratan Gupta24c85422019-01-30 19:41:24 +05301341 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001342 }
1343 if (property.first == "AccountUnlockTimeout")
1344 {
1345 const uint32_t* value =
1346 std::get_if<uint32_t>(&property.second);
1347 if (value != nullptr)
1348 {
1349 asyncResp->res
1350 .jsonValue["AccountLockoutDuration"] =
1351 *value;
1352 }
1353 }
1354 if (property.first == "MaxLoginAttemptBeforeLockout")
1355 {
1356 const uint16_t* value =
1357 std::get_if<uint16_t>(&property.second);
1358 if (value != nullptr)
1359 {
1360 asyncResp->res
1361 .jsonValue["AccountLockoutThreshold"] =
1362 *value;
1363 }
1364 }
1365 }
1366 },
1367 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1368 "org.freedesktop.DBus.Properties", "GetAll",
1369 "xyz.openbmc_project.User.AccountPolicy");
1370
1371 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
1372 const std::string& ldapType) {
1373 if (!success)
1374 {
1375 return;
1376 }
1377 parseLDAPConfigData(asyncResp->res.jsonValue, confData,
1378 ldapType);
1379 };
1380
1381 getLDAPConfigData("LDAP", callback);
1382 getLDAPConfigData("ActiveDirectory", callback);
1383 });
1384
Ed Tanousf5ffd802021-07-19 10:55:33 -07001385 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06001386 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07001387 .methods(boost::beast::http::verb::patch)(
1388 [](const crow::Request& req,
1389 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1390 std::optional<uint32_t> unlockTimeout;
1391 std::optional<uint16_t> lockoutThreshold;
1392 std::optional<uint16_t> minPasswordLength;
1393 std::optional<uint16_t> maxPasswordLength;
1394 std::optional<nlohmann::json> ldapObject;
1395 std::optional<nlohmann::json> activeDirectoryObject;
1396 std::optional<nlohmann::json> oemObject;
1397
1398 if (!json_util::readJson(
1399 req, asyncResp->res, "AccountLockoutDuration",
1400 unlockTimeout, "AccountLockoutThreshold",
1401 lockoutThreshold, "MaxPasswordLength",
1402 maxPasswordLength, "MinPasswordLength",
1403 minPasswordLength, "LDAP", ldapObject,
1404 "ActiveDirectory", activeDirectoryObject, "Oem",
1405 oemObject))
1406 {
1407 return;
1408 }
1409
1410 if (minPasswordLength)
1411 {
1412 messages::propertyNotWritable(asyncResp->res,
1413 "MinPasswordLength");
1414 }
1415
1416 if (maxPasswordLength)
1417 {
1418 messages::propertyNotWritable(asyncResp->res,
1419 "MaxPasswordLength");
1420 }
1421
1422 if (ldapObject)
1423 {
1424 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1425 }
1426
1427 if (std::optional<nlohmann::json> oemOpenBMCObject;
1428 oemObject &&
1429 json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1430 oemOpenBMCObject))
1431 {
1432 if (std::optional<nlohmann::json> authMethodsObject;
1433 oemOpenBMCObject &&
1434 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1435 "AuthMethods", authMethodsObject))
1436 {
1437 if (authMethodsObject)
1438 {
1439 handleAuthMethodsPatch(*authMethodsObject,
1440 asyncResp);
1441 }
1442 }
1443 }
1444
1445 if (activeDirectoryObject)
1446 {
1447 handleLDAPPatch(*activeDirectoryObject, asyncResp,
1448 "ActiveDirectory");
1449 }
1450
1451 if (unlockTimeout)
1452 {
1453 crow::connections::systemBus->async_method_call(
1454 [asyncResp](const boost::system::error_code ec) {
1455 if (ec)
1456 {
1457 messages::internalError(asyncResp->res);
1458 return;
1459 }
1460 messages::success(asyncResp->res);
1461 },
1462 "xyz.openbmc_project.User.Manager",
1463 "/xyz/openbmc_project/user",
1464 "org.freedesktop.DBus.Properties", "Set",
1465 "xyz.openbmc_project.User.AccountPolicy",
1466 "AccountUnlockTimeout",
Ed Tanous168e20c2021-12-13 14:39:53 -08001467 dbus::utility::DbusVariantType(*unlockTimeout));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001468 }
1469 if (lockoutThreshold)
1470 {
1471 crow::connections::systemBus->async_method_call(
1472 [asyncResp](const boost::system::error_code ec) {
1473 if (ec)
1474 {
1475 messages::internalError(asyncResp->res);
1476 return;
1477 }
1478 messages::success(asyncResp->res);
1479 },
1480 "xyz.openbmc_project.User.Manager",
1481 "/xyz/openbmc_project/user",
1482 "org.freedesktop.DBus.Properties", "Set",
1483 "xyz.openbmc_project.User.AccountPolicy",
1484 "MaxLoginAttemptBeforeLockout",
Ed Tanous168e20c2021-12-13 14:39:53 -08001485 dbus::utility::DbusVariantType(*lockoutThreshold));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001486 }
1487 });
1488
Ed Tanous6c51eab2021-06-03 12:30:29 -07001489 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001490 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001491 .methods(boost::beast::http::verb::get)(
1492 [](const crow::Request& req,
1493 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1494 asyncResp->res.jsonValue = {
1495 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
1496 {"@odata.type", "#ManagerAccountCollection."
1497 "ManagerAccountCollection"},
1498 {"Name", "Accounts Collection"},
1499 {"Description", "BMC User Accounts"}};
1500
Ed Tanous6c51eab2021-06-03 12:30:29 -07001501 Privileges effectiveUserPrivileges =
1502 redfish::getUserPrivileges(req.userRole);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001503
JunLin Chenf5e29f32021-12-08 16:47:04 +08001504 std::string thisUser;
1505 if (req.session)
1506 {
1507 thisUser = req.session->username;
1508 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001509 crow::connections::systemBus->async_method_call(
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001510 [asyncResp, thisUser, effectiveUserPrivileges](
1511 const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001512 const dbus::utility::ManagedObjectType& users) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001513 if (ec)
1514 {
1515 messages::internalError(asyncResp->res);
Ratan Gupta24c85422019-01-30 19:41:24 +05301516 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001517 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001518
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001519 bool userCanSeeAllAccounts =
1520 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001521 {"ConfigureUsers"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001522
1523 bool userCanSeeSelf =
1524 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001525 {"ConfigureSelf"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001526
Ed Tanous6c51eab2021-06-03 12:30:29 -07001527 nlohmann::json& memberArray =
1528 asyncResp->res.jsonValue["Members"];
1529 memberArray = nlohmann::json::array();
Ratan Gupta24c85422019-01-30 19:41:24 +05301530
Ed Tanous6c51eab2021-06-03 12:30:29 -07001531 for (auto& userpath : users)
1532 {
1533 std::string user = userpath.first.filename();
1534 if (user.empty())
Ratan Gupta24c85422019-01-30 19:41:24 +05301535 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301536 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001537 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1538
Ratan Gupta24c85422019-01-30 19:41:24 +05301539 return;
1540 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301541
Ed Tanous6c51eab2021-06-03 12:30:29 -07001542 // As clarified by Redfish here:
1543 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1544 // Users without ConfigureUsers, only see their own
1545 // account. Users with ConfigureUsers, see all
1546 // accounts.
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001547 if (userCanSeeAllAccounts ||
1548 (thisUser == user && userCanSeeSelf))
Ratan Gupta24c85422019-01-30 19:41:24 +05301549 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001550 memberArray.push_back(
1551 {{"@odata.id",
1552 "/redfish/v1/AccountService/Accounts/" +
1553 user}});
Ratan Gupta24c85422019-01-30 19:41:24 +05301554 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001555 }
1556 asyncResp->res.jsonValue["Members@odata.count"] =
1557 memberArray.size();
1558 },
1559 "xyz.openbmc_project.User.Manager",
1560 "/xyz/openbmc_project/user",
1561 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Ratan Gupta24c85422019-01-30 19:41:24 +05301562 });
Ed Tanous06e086d2018-09-19 17:19:52 -07001563
Ed Tanous6c51eab2021-06-03 12:30:29 -07001564 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001565 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001566 .methods(boost::beast::http::verb::post)([](const crow::Request& req,
1567 const std::shared_ptr<
1568 bmcweb::AsyncResp>&
1569 asyncResp) -> void {
1570 std::string username;
1571 std::string password;
1572 std::optional<std::string> roleId("User");
1573 std::optional<bool> enabled = true;
1574 if (!json_util::readJson(req, asyncResp->res, "UserName", username,
1575 "Password", password, "RoleId", roleId,
1576 "Enabled", enabled))
1577 {
1578 return;
1579 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001580
Ed Tanous6c51eab2021-06-03 12:30:29 -07001581 std::string priv = getPrivilegeFromRoleId(*roleId);
1582 if (priv.empty())
1583 {
1584 messages::propertyValueNotInList(asyncResp->res, *roleId,
1585 "RoleId");
1586 return;
1587 }
1588 // TODO: Following override will be reverted once support in
1589 // phosphor-user-manager is added. In order to avoid dependency
1590 // issues, this is added in bmcweb, which will removed, once
1591 // phosphor-user-manager supports priv-noaccess.
1592 if (priv == "priv-noaccess")
1593 {
1594 roleId = "";
1595 }
1596 else
1597 {
1598 roleId = priv;
1599 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001600
Ed Tanous6c51eab2021-06-03 12:30:29 -07001601 // Reading AllGroups property
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001602 sdbusplus::asio::getProperty<std::vector<std::string>>(
1603 *crow::connections::systemBus,
1604 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1605 "xyz.openbmc_project.User.Manager", "AllGroups",
Ed Tanous6c51eab2021-06-03 12:30:29 -07001606 [asyncResp, username, password{std::move(password)}, roleId,
Ed Tanous168e20c2021-12-13 14:39:53 -08001607 enabled](const boost::system::error_code ec,
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001608 const std::vector<std::string>& allGroupsList) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001609 if (ec)
1610 {
1611 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1612 messages::internalError(asyncResp->res);
1613 return;
1614 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001615
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001616 if (allGroupsList.empty())
Ed Tanous6c51eab2021-06-03 12:30:29 -07001617 {
1618 messages::internalError(asyncResp->res);
1619 return;
1620 }
1621
1622 crow::connections::systemBus->async_method_call(
1623 [asyncResp, username,
1624 password](const boost::system::error_code ec2,
1625 sdbusplus::message::message& m) {
1626 if (ec2)
1627 {
1628 userErrorMessageHandler(
1629 m.get_error(), asyncResp, username, "");
1630 return;
1631 }
1632
1633 if (pamUpdatePassword(username, password) !=
1634 PAM_SUCCESS)
1635 {
1636 // At this point we have a user that's been
1637 // created, but the password set
1638 // failed.Something is wrong, so delete the user
1639 // that we've already created
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301640 sdbusplus::message::object_path tempObjPath(
1641 rootUserDbusPath);
1642 tempObjPath /= username;
1643 const std::string userPath(tempObjPath);
1644
Ed Tanous6c51eab2021-06-03 12:30:29 -07001645 crow::connections::systemBus->async_method_call(
1646 [asyncResp, password](
1647 const boost::system::error_code ec3) {
1648 if (ec3)
1649 {
1650 messages::internalError(
1651 asyncResp->res);
1652 return;
1653 }
1654
1655 // If password is invalid
1656 messages::propertyValueFormatError(
1657 asyncResp->res, password,
1658 "Password");
1659 },
1660 "xyz.openbmc_project.User.Manager",
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301661 userPath,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001662 "xyz.openbmc_project.Object.Delete",
1663 "Delete");
1664
1665 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1666 return;
1667 }
1668
1669 messages::created(asyncResp->res);
1670 asyncResp->res.addHeader(
1671 "Location",
1672 "/redfish/v1/AccountService/Accounts/" +
1673 username);
1674 },
1675 "xyz.openbmc_project.User.Manager",
1676 "/xyz/openbmc_project/user",
1677 "xyz.openbmc_project.User.Manager", "CreateUser",
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001678 username, allGroupsList, *roleId, *enabled);
1679 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001680 });
1681
1682 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001683 .privileges(redfish::privileges::getManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001684 .methods(
1685 boost::beast::http::verb::
1686 get)([](const crow::Request& req,
1687 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1688 const std::string& accountName) -> void {
1689 if (req.session->username != accountName)
1690 {
1691 // At this point we've determined that the user is trying to
1692 // modify a user that isn't them. We need to verify that they
1693 // have permissions to modify other users, so re-run the auth
1694 // check with the same permissions, minus ConfigureSelf.
1695 Privileges effectiveUserPrivileges =
1696 redfish::getUserPrivileges(req.userRole);
1697 Privileges requiredPermissionsToChangeNonSelf = {
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001698 "ConfigureUsers", "ConfigureManager"};
Ed Tanous6c51eab2021-06-03 12:30:29 -07001699 if (!effectiveUserPrivileges.isSupersetOf(
1700 requiredPermissionsToChangeNonSelf))
Ed Tanous06e086d2018-09-19 17:19:52 -07001701 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001702 BMCWEB_LOG_DEBUG << "GET Account denied access";
1703 messages::insufficientPrivilege(asyncResp->res);
1704 return;
1705 }
1706 }
1707
1708 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -08001709 [asyncResp,
1710 accountName](const boost::system::error_code ec,
1711 const dbus::utility::ManagedObjectType& users) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001712 if (ec)
1713 {
1714 messages::internalError(asyncResp->res);
1715 return;
1716 }
Ed Tanous711ac7a2021-12-20 09:34:41 -08001717 const auto userIt = std::find_if(
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301718 users.begin(), users.end(),
1719 [accountName](
1720 const std::pair<sdbusplus::message::object_path,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001721 dbus::utility::DBusInteracesMap>&
1722 user) {
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301723 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