blob: 5cd70bc2f968f289ffdba268d00d65723a4b6fec [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
Ratan Gupta6973a582018-12-13 18:25:44 +053070using GetObjectType =
71 std::vector<std::pair<std::string, std::vector<std::string>>>;
AppaRao Puli84e12cb2018-10-11 01:28:15 +053072
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060073inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053074{
75 if (role == "priv-admin")
76 {
77 return "Administrator";
78 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070079 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053080 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053081 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053082 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070083 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053084 {
85 return "Operator";
86 }
Ed Tanous26f69762022-01-25 09:49:11 -080087 if (role.empty() || (role == "priv-noaccess"))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +000088 {
89 return "NoAccess";
90 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +053091 return "";
92}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060093inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053094{
95 if (role == "Administrator")
96 {
97 return "priv-admin";
98 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070099 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530100 {
101 return "priv-user";
102 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700103 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530104 {
105 return "priv-operator";
106 }
Ed Tanous26f69762022-01-25 09:49:11 -0800107 if ((role == "NoAccess") || (role.empty()))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +0000108 {
109 return "priv-noaccess";
110 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530111 return "";
112}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700113
zhanghch058d1b46d2021-04-01 11:18:24 +0800114inline void userErrorMessageHandler(
115 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
116 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000117{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000118 if (e == nullptr)
119 {
120 messages::internalError(asyncResp->res);
121 return;
122 }
123
Manojkiran Eda055806b2020-11-03 09:36:28 +0530124 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000125 if (strcmp(errorMessage,
126 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
127 {
128 messages::resourceAlreadyExists(asyncResp->res,
Gunnar Mills8114bd42020-06-11 20:55:21 -0500129 "#ManagerAccount.v1_4_0.ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000130 "UserName", newUser);
131 }
132 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
133 "UserNameDoesNotExist") == 0)
134 {
135 messages::resourceNotFound(
Gunnar Mills8114bd42020-06-11 20:55:21 -0500136 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000137 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700138 else if ((strcmp(errorMessage,
139 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
140 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800141 (strcmp(
142 errorMessage,
143 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
144 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000145 {
146 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
147 }
148 else if (strcmp(errorMessage,
149 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
150 {
151 messages::createLimitReachedForResource(asyncResp->res);
152 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000153 else
154 {
155 messages::internalError(asyncResp->res);
156 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000157}
158
Ed Tanous81ce6092020-12-17 16:54:55 +0000159inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000160 const LDAPConfigData& confData,
161 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530162{
Ratan Guptaab828d72019-04-22 14:18:33 +0530163 std::string service =
164 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600165 nlohmann::json ldap = {
Ratan Gupta6973a582018-12-13 18:25:44 +0530166 {"ServiceEnabled", confData.serviceEnabled},
167 {"ServiceAddresses", nlohmann::json::array({confData.uri})},
168 {"Authentication",
169 {{"AuthenticationType", "UsernameAndPassword"},
Ratan Gupta6973a582018-12-13 18:25:44 +0530170 {"Username", confData.bindDN},
171 {"Password", nullptr}}},
172 {"LDAPService",
173 {{"SearchSettings",
174 {{"BaseDistinguishedNames",
175 nlohmann::json::array({confData.baseDN})},
176 {"UsernameAttribute", confData.userNameAttribute},
177 {"GroupsAttribute", confData.groupAttribute}}}}},
178 };
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600179
Ed Tanous81ce6092020-12-17 16:54:55 +0000180 jsonResponse[ldapType].update(ldap);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600181
Ed Tanous81ce6092020-12-17 16:54:55 +0000182 nlohmann::json& roleMapArray = jsonResponse[ldapType]["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600183 roleMapArray = nlohmann::json::array();
Ed Tanous9eb808c2022-01-25 10:19:23 -0800184 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600185 {
186 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
187 << obj.second.groupName << "\n";
188 roleMapArray.push_back(
189 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
190 nlohmann::json::array(
191 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
192 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530193}
194
195/**
Ratan Gupta06785242019-07-26 22:30:16 +0530196 * @brief validates given JSON input and then calls appropriate method to
197 * create, to delete or to set Rolemapping object based on the given input.
198 *
199 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000200inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800201 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530202 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700203 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530204{
205 for (size_t index = 0; index < input.size(); index++)
206 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700207 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530208
209 if (thisJson.is_null())
210 {
211 // delete the existing object
212 if (index < roleMapObjData.size())
213 {
214 crow::connections::systemBus->async_method_call(
215 [asyncResp, roleMapObjData, serverType,
216 index](const boost::system::error_code ec) {
217 if (ec)
218 {
219 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
220 messages::internalError(asyncResp->res);
221 return;
222 }
223 asyncResp->res
224 .jsonValue[serverType]["RemoteRoleMapping"][index] =
225 nullptr;
226 },
227 ldapDbusService, roleMapObjData[index].first,
228 "xyz.openbmc_project.Object.Delete", "Delete");
229 }
230 else
231 {
232 BMCWEB_LOG_ERROR << "Can't delete the object";
233 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800234 asyncResp->res,
235 thisJson.dump(2, ' ', true,
236 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530237 "RemoteRoleMapping/" + std::to_string(index));
238 return;
239 }
240 }
241 else if (thisJson.empty())
242 {
243 // Don't do anything for the empty objects,parse next json
244 // eg {"RemoteRoleMapping",[{}]}
245 }
246 else
247 {
248 // update/create the object
249 std::optional<std::string> remoteGroup;
250 std::optional<std::string> localRole;
251
Ed Tanousf23b7292020-10-15 09:41:17 -0700252 // This is a copy, but it's required in this case because of how
253 // readJson is structured
254 nlohmann::json thisJsonCopy = thisJson;
255 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
256 "RemoteGroup", remoteGroup, "LocalRole",
257 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530258 {
259 continue;
260 }
261
262 // Update existing RoleMapping Object
263 if (index < roleMapObjData.size())
264 {
265 BMCWEB_LOG_DEBUG << "Update Role Map Object";
266 // If "RemoteGroup" info is provided
267 if (remoteGroup)
268 {
269 crow::connections::systemBus->async_method_call(
270 [asyncResp, roleMapObjData, serverType, index,
271 remoteGroup](const boost::system::error_code ec) {
272 if (ec)
273 {
274 BMCWEB_LOG_ERROR << "DBUS response error: "
275 << ec;
276 messages::internalError(asyncResp->res);
277 return;
278 }
279 asyncResp->res
280 .jsonValue[serverType]["RemoteRoleMapping"]
281 [index]["RemoteGroup"] = *remoteGroup;
282 },
283 ldapDbusService, roleMapObjData[index].first,
284 propertyInterface, "Set",
285 "xyz.openbmc_project.User.PrivilegeMapperEntry",
286 "GroupName",
Ed Tanous168e20c2021-12-13 14:39:53 -0800287 dbus::utility::DbusVariantType(
288 std::move(*remoteGroup)));
Ratan Gupta06785242019-07-26 22:30:16 +0530289 }
290
291 // If "LocalRole" info is provided
292 if (localRole)
293 {
294 crow::connections::systemBus->async_method_call(
295 [asyncResp, roleMapObjData, serverType, index,
296 localRole](const boost::system::error_code ec) {
297 if (ec)
298 {
299 BMCWEB_LOG_ERROR << "DBUS response error: "
300 << ec;
301 messages::internalError(asyncResp->res);
302 return;
303 }
304 asyncResp->res
305 .jsonValue[serverType]["RemoteRoleMapping"]
306 [index]["LocalRole"] = *localRole;
307 },
308 ldapDbusService, roleMapObjData[index].first,
309 propertyInterface, "Set",
310 "xyz.openbmc_project.User.PrivilegeMapperEntry",
311 "Privilege",
Ed Tanous168e20c2021-12-13 14:39:53 -0800312 dbus::utility::DbusVariantType(
Ratan Gupta06785242019-07-26 22:30:16 +0530313 getPrivilegeFromRoleId(std::move(*localRole))));
314 }
315 }
316 // Create a new RoleMapping Object.
317 else
318 {
319 BMCWEB_LOG_DEBUG
320 << "setRoleMappingProperties: Creating new Object";
321 std::string pathString =
322 "RemoteRoleMapping/" + std::to_string(index);
323
324 if (!localRole)
325 {
326 messages::propertyMissing(asyncResp->res,
327 pathString + "/LocalRole");
328 continue;
329 }
330 if (!remoteGroup)
331 {
332 messages::propertyMissing(asyncResp->res,
333 pathString + "/RemoteGroup");
334 continue;
335 }
336
337 std::string dbusObjectPath;
338 if (serverType == "ActiveDirectory")
339 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700340 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530341 }
342 else if (serverType == "LDAP")
343 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000344 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530345 }
346
347 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
348 << ",LocalRole=" << *localRole;
349
350 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700351 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530352 remoteGroup](const boost::system::error_code ec) {
353 if (ec)
354 {
355 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
356 messages::internalError(asyncResp->res);
357 return;
358 }
359 nlohmann::json& remoteRoleJson =
360 asyncResp->res
361 .jsonValue[serverType]["RemoteRoleMapping"];
362 remoteRoleJson.push_back(
363 {{"LocalRole", *localRole},
364 {"RemoteGroup", *remoteGroup}});
365 },
366 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700367 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530368 getPrivilegeFromRoleId(std::move(*localRole)));
369 }
370 }
371 }
372}
373
374/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530375 * Function that retrieves all properties for LDAP config object
376 * into JSON
377 */
378template <typename CallbackFunc>
379inline void getLDAPConfigData(const std::string& ldapType,
380 CallbackFunc&& callback)
381{
Ratan Guptaab828d72019-04-22 14:18:33 +0530382
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600383 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530384 ldapConfigInterface};
385
386 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600387 [callback, ldapType](const boost::system::error_code ec,
388 const GetObjectType& resp) {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600389 if (ec || resp.empty())
390 {
George Liu0fda0f12021-11-16 10:06:17 +0800391 BMCWEB_LOG_ERROR
392 << "DBUS response error during getting of service name: "
393 << ec;
Ed Tanous23a21a12020-07-25 04:45:05 +0000394 LDAPConfigData empty{};
395 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600396 return;
397 }
398 std::string service = resp.begin()->first;
399 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800400 [callback, ldapType](
401 const boost::system::error_code errorCode,
402 const dbus::utility::ManagedObjectType& ldapObjects) {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600403 LDAPConfigData confData{};
Ed Tanous81ce6092020-12-17 16:54:55 +0000404 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600405 {
406 callback(false, confData, ldapType);
407 BMCWEB_LOG_ERROR << "D-Bus responses error: "
Ed Tanous81ce6092020-12-17 16:54:55 +0000408 << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600409 return;
410 }
411
412 std::string ldapDbusType;
413 std::string searchString;
414
415 if (ldapType == "LDAP")
416 {
George Liu0fda0f12021-11-16 10:06:17 +0800417 ldapDbusType =
418 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600419 searchString = "openldap";
420 }
421 else if (ldapType == "ActiveDirectory")
422 {
423 ldapDbusType =
George Liu0fda0f12021-11-16 10:06:17 +0800424 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600425 searchString = "active_directory";
426 }
427 else
428 {
429 BMCWEB_LOG_ERROR
430 << "Can't get the DbusType for the given type="
431 << ldapType;
432 callback(false, confData, ldapType);
433 return;
434 }
435
436 std::string ldapEnableInterfaceStr = ldapEnableInterface;
437 std::string ldapConfigInterfaceStr = ldapConfigInterface;
438
439 for (const auto& object : ldapObjects)
440 {
441 // let's find the object whose ldap type is equal to the
442 // given type
443 if (object.first.str.find(searchString) ==
444 std::string::npos)
445 {
446 continue;
447 }
448
449 for (const auto& interface : object.second)
450 {
451 if (interface.first == ldapEnableInterfaceStr)
452 {
453 // rest of the properties are string.
454 for (const auto& property : interface.second)
455 {
456 if (property.first == "Enabled")
457 {
458 const bool* value =
459 std::get_if<bool>(&property.second);
460 if (value == nullptr)
461 {
462 continue;
463 }
464 confData.serviceEnabled = *value;
465 break;
466 }
467 }
468 }
469 else if (interface.first == ldapConfigInterfaceStr)
470 {
471
472 for (const auto& property : interface.second)
473 {
Ed Tanous271584a2019-07-09 16:24:22 -0700474 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600475 std::get_if<std::string>(
476 &property.second);
Ed Tanous271584a2019-07-09 16:24:22 -0700477 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600478 {
479 continue;
480 }
481 if (property.first == "LDAPServerURI")
482 {
Ed Tanous271584a2019-07-09 16:24:22 -0700483 confData.uri = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600484 }
485 else if (property.first == "LDAPBindDN")
486 {
Ed Tanous271584a2019-07-09 16:24:22 -0700487 confData.bindDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600488 }
489 else if (property.first == "LDAPBaseDN")
490 {
Ed Tanous271584a2019-07-09 16:24:22 -0700491 confData.baseDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600492 }
493 else if (property.first ==
494 "LDAPSearchScope")
495 {
Ed Tanous271584a2019-07-09 16:24:22 -0700496 confData.searchScope = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600497 }
498 else if (property.first ==
499 "GroupNameAttribute")
500 {
Ed Tanous271584a2019-07-09 16:24:22 -0700501 confData.groupAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600502 }
503 else if (property.first ==
504 "UserNameAttribute")
505 {
Ed Tanous271584a2019-07-09 16:24:22 -0700506 confData.userNameAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600507 }
508 else if (property.first == "LDAPType")
509 {
Ed Tanous271584a2019-07-09 16:24:22 -0700510 confData.serverType = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600511 }
512 }
513 }
George Liu0fda0f12021-11-16 10:06:17 +0800514 else if (
515 interface.first ==
516 "xyz.openbmc_project.User.PrivilegeMapperEntry")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600517 {
518 LDAPRoleMapData roleMapData{};
519 for (const auto& property : interface.second)
520 {
Ed Tanous271584a2019-07-09 16:24:22 -0700521 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600522 std::get_if<std::string>(
523 &property.second);
524
Ed Tanous271584a2019-07-09 16:24:22 -0700525 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600526 {
527 continue;
528 }
529
530 if (property.first == "GroupName")
531 {
Ed Tanous271584a2019-07-09 16:24:22 -0700532 roleMapData.groupName = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600533 }
534 else if (property.first == "Privilege")
535 {
Ed Tanous271584a2019-07-09 16:24:22 -0700536 roleMapData.privilege = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600537 }
538 }
539
Ed Tanous0f0353b2019-10-24 11:37:51 -0700540 confData.groupRoleList.emplace_back(
541 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600542 }
543 }
544 }
545 callback(true, confData, ldapType);
546 },
547 service, ldapRootObject, dbusObjManagerIntf,
548 "GetManagedObjects");
549 },
550 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
Ed Tanous23a21a12020-07-25 04:45:05 +0000551 ldapConfigObjectName, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530552}
553
Ed Tanous6c51eab2021-06-03 12:30:29 -0700554/**
555 * @brief parses the authentication section under the LDAP
556 * @param input JSON data
557 * @param asyncResp pointer to the JSON response
558 * @param userName userName to be filled from the given JSON.
559 * @param password password to be filled from the given JSON.
560 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700561inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700562 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
563 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700564{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700565 std::optional<std::string> authType;
566
567 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
568 authType, "Username", username, "Password",
569 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700570 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700571 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700573 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530574 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700575 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530576 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700577 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530578 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700579 messages::propertyValueNotInList(asyncResp->res, *authType,
580 "AuthenticationType");
581 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530582 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700583}
584/**
585 * @brief parses the LDAPService section under the LDAP
586 * @param input JSON data
587 * @param asyncResp pointer to the JSON response
588 * @param baseDNList baseDN to be filled from the given JSON.
589 * @param userNameAttribute userName to be filled from the given JSON.
590 * @param groupaAttribute password to be filled from the given JSON.
591 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530592
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700593inline void
594 parseLDAPServiceJson(nlohmann::json input,
595 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
596 std::optional<std::vector<std::string>>& baseDNList,
597 std::optional<std::string>& userNameAttribute,
598 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700599{
600 std::optional<nlohmann::json> searchSettings;
601
602 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
603 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530604 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700605 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530606 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700607 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530608 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700609 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530610 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700611 if (!json_util::readJson(*searchSettings, asyncResp->res,
612 "BaseDistinguishedNames", baseDNList,
613 "UsernameAttribute", userNameAttribute,
614 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530615 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700616 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530617 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700618}
619/**
620 * @brief updates the LDAP server address and updates the
621 json response with the new value.
622 * @param serviceAddressList address to be updated.
623 * @param asyncResp pointer to the JSON response
624 * @param ldapServerElementName Type of LDAP
625 server(openLDAP/ActiveDirectory)
626 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530627
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700628inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700629 const std::vector<std::string>& serviceAddressList,
630 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
631 const std::string& ldapServerElementName,
632 const std::string& ldapConfigObject)
633{
634 crow::connections::systemBus->async_method_call(
635 [asyncResp, ldapServerElementName,
636 serviceAddressList](const boost::system::error_code ec) {
637 if (ec)
638 {
639 BMCWEB_LOG_DEBUG
640 << "Error Occurred in updating the service address";
641 messages::internalError(asyncResp->res);
642 return;
643 }
644 std::vector<std::string> modifiedserviceAddressList = {
645 serviceAddressList.front()};
646 asyncResp->res
647 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
648 modifiedserviceAddressList;
649 if ((serviceAddressList).size() > 1)
650 {
651 messages::propertyValueModified(asyncResp->res,
652 "ServiceAddresses",
653 serviceAddressList.front());
654 }
655 BMCWEB_LOG_DEBUG << "Updated the service address";
656 },
657 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
658 ldapConfigInterface, "LDAPServerURI",
Ed Tanous168e20c2021-12-13 14:39:53 -0800659 dbus::utility::DbusVariantType(serviceAddressList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700660}
661/**
662 * @brief updates the LDAP Bind DN and updates the
663 json response with the new value.
664 * @param username name of the user which needs to be updated.
665 * @param asyncResp pointer to the JSON response
666 * @param ldapServerElementName Type of LDAP
667 server(openLDAP/ActiveDirectory)
668 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530669
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700670inline void
671 handleUserNamePatch(const std::string& username,
672 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
673 const std::string& ldapServerElementName,
674 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700675{
676 crow::connections::systemBus->async_method_call(
677 [asyncResp, username,
678 ldapServerElementName](const boost::system::error_code ec) {
679 if (ec)
680 {
681 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
682 messages::internalError(asyncResp->res);
683 return;
684 }
685 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
686 ["Username"] = username;
687 BMCWEB_LOG_DEBUG << "Updated the username";
688 },
689 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800690 ldapConfigInterface, "LDAPBindDN",
691 dbus::utility::DbusVariantType(username));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700692}
693
694/**
695 * @brief updates the LDAP password
696 * @param password : ldap password which needs to be updated.
697 * @param asyncResp pointer to the JSON response
698 * @param ldapServerElementName Type of LDAP
699 * server(openLDAP/ActiveDirectory)
700 */
701
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700702inline void
703 handlePasswordPatch(const std::string& password,
704 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
705 const std::string& ldapServerElementName,
706 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700707{
708 crow::connections::systemBus->async_method_call(
709 [asyncResp, password,
710 ldapServerElementName](const boost::system::error_code ec) {
711 if (ec)
712 {
713 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
714 messages::internalError(asyncResp->res);
715 return;
716 }
717 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
718 ["Password"] = "";
719 BMCWEB_LOG_DEBUG << "Updated the password";
720 },
721 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
722 ldapConfigInterface, "LDAPBindDNPassword",
Ed Tanous168e20c2021-12-13 14:39:53 -0800723 dbus::utility::DbusVariantType(password));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700724}
725
726/**
727 * @brief updates the LDAP BaseDN and updates the
728 json response with the new value.
729 * @param baseDNList baseDN list which needs to be updated.
730 * @param asyncResp pointer to the JSON response
731 * @param ldapServerElementName Type of LDAP
732 server(openLDAP/ActiveDirectory)
733 */
734
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700735inline void
736 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
737 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
738 const std::string& ldapServerElementName,
739 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700740{
741 crow::connections::systemBus->async_method_call(
742 [asyncResp, baseDNList,
743 ldapServerElementName](const boost::system::error_code ec) {
744 if (ec)
745 {
746 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
747 messages::internalError(asyncResp->res);
748 return;
749 }
750 auto& serverTypeJson =
751 asyncResp->res.jsonValue[ldapServerElementName];
752 auto& searchSettingsJson =
753 serverTypeJson["LDAPService"]["SearchSettings"];
754 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
755 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
756 if (baseDNList.size() > 1)
757 {
758 messages::propertyValueModified(asyncResp->res,
759 "BaseDistinguishedNames",
760 baseDNList.front());
761 }
762 BMCWEB_LOG_DEBUG << "Updated the base DN";
763 },
764 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
765 ldapConfigInterface, "LDAPBaseDN",
Ed Tanous168e20c2021-12-13 14:39:53 -0800766 dbus::utility::DbusVariantType(baseDNList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700767}
768/**
769 * @brief updates the LDAP user name attribute and updates the
770 json response with the new value.
771 * @param userNameAttribute attribute to be updated.
772 * @param asyncResp pointer to the JSON response
773 * @param ldapServerElementName Type of LDAP
774 server(openLDAP/ActiveDirectory)
775 */
776
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700777inline void
778 handleUserNameAttrPatch(const std::string& userNameAttribute,
779 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
780 const std::string& ldapServerElementName,
781 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700782{
783 crow::connections::systemBus->async_method_call(
784 [asyncResp, userNameAttribute,
785 ldapServerElementName](const boost::system::error_code ec) {
786 if (ec)
787 {
788 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
789 "username attribute";
790 messages::internalError(asyncResp->res);
791 return;
792 }
793 auto& serverTypeJson =
794 asyncResp->res.jsonValue[ldapServerElementName];
795 auto& searchSettingsJson =
796 serverTypeJson["LDAPService"]["SearchSettings"];
797 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
798 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
799 },
800 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
801 ldapConfigInterface, "UserNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800802 dbus::utility::DbusVariantType(userNameAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700803}
804/**
805 * @brief updates the LDAP group attribute and updates the
806 json response with the new value.
807 * @param groupsAttribute attribute to be updated.
808 * @param asyncResp pointer to the JSON response
809 * @param ldapServerElementName Type of LDAP
810 server(openLDAP/ActiveDirectory)
811 */
812
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700813inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700814 const std::string& groupsAttribute,
815 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
816 const std::string& ldapServerElementName,
817 const std::string& ldapConfigObject)
818{
819 crow::connections::systemBus->async_method_call(
820 [asyncResp, groupsAttribute,
821 ldapServerElementName](const boost::system::error_code ec) {
822 if (ec)
823 {
824 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
825 "groupname attribute";
826 messages::internalError(asyncResp->res);
827 return;
828 }
829 auto& serverTypeJson =
830 asyncResp->res.jsonValue[ldapServerElementName];
831 auto& searchSettingsJson =
832 serverTypeJson["LDAPService"]["SearchSettings"];
833 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
834 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
835 },
836 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
837 ldapConfigInterface, "GroupNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800838 dbus::utility::DbusVariantType(groupsAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700839}
840/**
841 * @brief updates the LDAP service enable and updates the
842 json response with the new value.
843 * @param input JSON data.
844 * @param asyncResp pointer to the JSON response
845 * @param ldapServerElementName Type of LDAP
846 server(openLDAP/ActiveDirectory)
847 */
848
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700849inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700850 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
851 const std::string& ldapServerElementName,
852 const std::string& ldapConfigObject)
853{
854 crow::connections::systemBus->async_method_call(
855 [asyncResp, serviceEnabled,
856 ldapServerElementName](const boost::system::error_code ec) {
857 if (ec)
858 {
859 BMCWEB_LOG_DEBUG
860 << "Error Occurred in Updating the service enable";
861 messages::internalError(asyncResp->res);
862 return;
863 }
864 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
865 serviceEnabled;
866 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
867 },
868 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800869 ldapEnableInterface, "Enabled",
870 dbus::utility::DbusVariantType(serviceEnabled));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700871}
872
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700873inline void
874 handleAuthMethodsPatch(nlohmann::json& input,
875 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700876{
877 std::optional<bool> basicAuth;
878 std::optional<bool> cookie;
879 std::optional<bool> sessionToken;
880 std::optional<bool> xToken;
881 std::optional<bool> tls;
882
883 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
884 "Cookie", cookie, "SessionToken", sessionToken,
885 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530886 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700887 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
888 return;
889 }
890
891 // Make a copy of methods configuration
892 persistent_data::AuthConfigMethods authMethodsConfig =
893 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
894
895 if (basicAuth)
896 {
897#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
898 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800899 asyncResp->res,
900 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700901 return;
902#endif
903 authMethodsConfig.basic = *basicAuth;
904 }
905
906 if (cookie)
907 {
908#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800909 messages::actionNotSupported(
910 asyncResp->res,
911 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700912 return;
913#endif
914 authMethodsConfig.cookie = *cookie;
915 }
916
917 if (sessionToken)
918 {
919#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
920 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800921 asyncResp->res,
922 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700923 return;
924#endif
925 authMethodsConfig.sessionToken = *sessionToken;
926 }
927
928 if (xToken)
929 {
930#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800931 messages::actionNotSupported(
932 asyncResp->res,
933 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700934 return;
935#endif
936 authMethodsConfig.xtoken = *xToken;
937 }
938
939 if (tls)
940 {
941#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800942 messages::actionNotSupported(
943 asyncResp->res,
944 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700945 return;
946#endif
947 authMethodsConfig.tls = *tls;
948 }
949
950 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
951 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
952 !authMethodsConfig.tls)
953 {
954 // Do not allow user to disable everything
955 messages::actionNotSupported(asyncResp->res,
956 "of disabling all available methods");
957 return;
958 }
959
960 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
961 authMethodsConfig);
962 // Save configuration immediately
963 persistent_data::getConfig().writeData();
964
965 messages::success(asyncResp->res);
966}
967
968/**
969 * @brief Get the required values from the given JSON, validates the
970 * value and create the LDAP config object.
971 * @param input JSON data
972 * @param asyncResp pointer to the JSON response
973 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
974 */
975
976inline void handleLDAPPatch(nlohmann::json& input,
977 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
978 const std::string& serverType)
979{
980 std::string dbusObjectPath;
981 if (serverType == "ActiveDirectory")
982 {
983 dbusObjectPath = adConfigObject;
984 }
985 else if (serverType == "LDAP")
986 {
987 dbusObjectPath = ldapConfigObjectName;
988 }
989 else
990 {
991 return;
992 }
993
994 std::optional<nlohmann::json> authentication;
995 std::optional<nlohmann::json> ldapService;
996 std::optional<std::vector<std::string>> serviceAddressList;
997 std::optional<bool> serviceEnabled;
998 std::optional<std::vector<std::string>> baseDNList;
999 std::optional<std::string> userNameAttribute;
1000 std::optional<std::string> groupsAttribute;
1001 std::optional<std::string> userName;
1002 std::optional<std::string> password;
1003 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1004
1005 if (!json_util::readJson(input, asyncResp->res, "Authentication",
1006 authentication, "LDAPService", ldapService,
1007 "ServiceAddresses", serviceAddressList,
1008 "ServiceEnabled", serviceEnabled,
1009 "RemoteRoleMapping", remoteRoleMapData))
1010 {
1011 return;
1012 }
1013
1014 if (authentication)
1015 {
1016 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1017 password);
1018 }
1019 if (ldapService)
1020 {
1021 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1022 userNameAttribute, groupsAttribute);
1023 }
1024 if (serviceAddressList)
1025 {
Ed Tanous26f69762022-01-25 09:49:11 -08001026 if (serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301027 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001028 messages::propertyValueNotInList(asyncResp->res, "[]",
1029 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001030 return;
1031 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001032 }
1033 if (baseDNList)
1034 {
Ed Tanous26f69762022-01-25 09:49:11 -08001035 if (baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +05301036 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001037 messages::propertyValueNotInList(asyncResp->res, "[]",
1038 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301039 return;
1040 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001041 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301042
Ed Tanous6c51eab2021-06-03 12:30:29 -07001043 // nothing to update, then return
1044 if (!userName && !password && !serviceAddressList && !baseDNList &&
1045 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1046 !remoteRoleMapData)
1047 {
1048 return;
1049 }
1050
1051 // Get the existing resource first then keep modifying
1052 // whenever any property gets updated.
1053 getLDAPConfigData(serverType, [asyncResp, userName, password, baseDNList,
1054 userNameAttribute, groupsAttribute,
1055 serviceAddressList, serviceEnabled,
1056 dbusObjectPath, remoteRoleMapData](
1057 bool success,
1058 const LDAPConfigData& confData,
1059 const std::string& serverT) {
1060 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301061 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001062 messages::internalError(asyncResp->res);
1063 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301064 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001065 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1066 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301067 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001068 // Disable the service first and update the rest of
1069 // the properties.
1070 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301071 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001072
Ratan Gupta8a07d282019-03-16 08:33:47 +05301073 if (serviceAddressList)
1074 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001075 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1076 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301077 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001078 if (userName)
1079 {
1080 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1081 }
1082 if (password)
1083 {
1084 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1085 }
1086
Ratan Gupta8a07d282019-03-16 08:33:47 +05301087 if (baseDNList)
1088 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001089 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1090 }
1091 if (userNameAttribute)
1092 {
1093 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1094 dbusObjectPath);
1095 }
1096 if (groupsAttribute)
1097 {
1098 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1099 dbusObjectPath);
1100 }
1101 if (serviceEnabled)
1102 {
1103 // if user has given the value as true then enable
1104 // the service. if user has given false then no-op
1105 // as service is already stopped.
1106 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301107 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001108 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1109 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301110 }
1111 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001112 else
1113 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001114 // if user has not given the service enabled value
1115 // then revert it to the same state as it was
1116 // before.
1117 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1118 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001119 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001120
Ed Tanous6c51eab2021-06-03 12:30:29 -07001121 if (remoteRoleMapData)
1122 {
1123 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1124 *remoteRoleMapData);
1125 }
1126 });
1127}
1128
1129inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1130 const std::string& username,
1131 std::optional<std::string> password,
1132 std::optional<bool> enabled,
1133 std::optional<std::string> roleId,
1134 std::optional<bool> locked)
1135{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301136 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1137 tempObjPath /= username;
1138 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001139
1140 dbus::utility::checkDbusPathExists(
1141 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001142 [dbusObjectPath, username, password(std::move(password)),
1143 roleId(std::move(roleId)), enabled, locked,
1144 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanouse662eae2022-01-25 10:39:19 -08001145 if (rc <= 0)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001146 {
1147 messages::resourceNotFound(
1148 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1149 username);
1150 return;
1151 }
1152
1153 if (password)
1154 {
1155 int retval = pamUpdatePassword(username, *password);
1156
1157 if (retval == PAM_USER_UNKNOWN)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001158 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001159 messages::resourceNotFound(
1160 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1161 username);
1162 }
1163 else if (retval == PAM_AUTHTOK_ERR)
1164 {
1165 // If password is invalid
1166 messages::propertyValueFormatError(asyncResp->res,
1167 *password, "Password");
1168 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1169 }
1170 else if (retval != PAM_SUCCESS)
1171 {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001172 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001173 return;
1174 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001175 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001176
Ed Tanous6c51eab2021-06-03 12:30:29 -07001177 if (enabled)
1178 {
1179 crow::connections::systemBus->async_method_call(
1180 [asyncResp](const boost::system::error_code ec) {
1181 if (ec)
1182 {
1183 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1184 messages::internalError(asyncResp->res);
1185 return;
1186 }
1187 messages::success(asyncResp->res);
1188 return;
1189 },
Ed Tanouse05aec52022-01-25 10:28:56 -08001190 "xyz.openbmc_project.User.Manager", dbusObjectPath,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001191 "org.freedesktop.DBus.Properties", "Set",
1192 "xyz.openbmc_project.User.Attributes", "UserEnabled",
Ed Tanous168e20c2021-12-13 14:39:53 -08001193 dbus::utility::DbusVariantType{*enabled});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001194 }
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001195
Ed Tanous6c51eab2021-06-03 12:30:29 -07001196 if (roleId)
1197 {
1198 std::string priv = getPrivilegeFromRoleId(*roleId);
1199 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001200 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001201 messages::propertyValueNotInList(asyncResp->res, *roleId,
1202 "RoleId");
1203 return;
1204 }
1205 if (priv == "priv-noaccess")
1206 {
1207 priv = "";
1208 }
1209
1210 crow::connections::systemBus->async_method_call(
1211 [asyncResp](const boost::system::error_code ec) {
1212 if (ec)
1213 {
1214 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1215 messages::internalError(asyncResp->res);
1216 return;
1217 }
1218 messages::success(asyncResp->res);
1219 },
Ed Tanouse05aec52022-01-25 10:28:56 -08001220 "xyz.openbmc_project.User.Manager", dbusObjectPath,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001221 "org.freedesktop.DBus.Properties", "Set",
1222 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
Ed Tanous168e20c2021-12-13 14:39:53 -08001223 dbus::utility::DbusVariantType{priv});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001224 }
1225
1226 if (locked)
1227 {
1228 // admin can unlock the account which is locked by
1229 // successive authentication failures but admin should
1230 // not be allowed to lock an account.
1231 if (*locked)
1232 {
1233 messages::propertyValueNotInList(asyncResp->res, "true",
1234 "Locked");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001235 return;
1236 }
1237
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001238 crow::connections::systemBus->async_method_call(
Ed Tanous6c51eab2021-06-03 12:30:29 -07001239 [asyncResp](const boost::system::error_code ec) {
1240 if (ec)
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001241 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001242 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1243 messages::internalError(asyncResp->res);
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001244 return;
1245 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001246 messages::success(asyncResp->res);
1247 return;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001248 },
Ed Tanouse05aec52022-01-25 10:28:56 -08001249 "xyz.openbmc_project.User.Manager", dbusObjectPath,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001250 "org.freedesktop.DBus.Properties", "Set",
1251 "xyz.openbmc_project.User.Attributes",
Ed Tanous168e20c2021-12-13 14:39:53 -08001252 "UserLockedForFailedAttempt",
1253 dbus::utility::DbusVariantType{*locked});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001254 }
1255 });
1256}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001257
Ed Tanous6c51eab2021-06-03 12:30:29 -07001258inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001259{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001260
Ed Tanous6c51eab2021-06-03 12:30:29 -07001261 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001262 .privileges(redfish::privileges::getAccountService)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001263 .methods(
Abhishek Patel72048782021-06-02 09:53:24 -05001264 boost::beast::http::verb::get)([](const crow::Request& req,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001265 const std::shared_ptr<
1266 bmcweb::AsyncResp>& asyncResp)
1267 -> void {
1268 const persistent_data::AuthConfigMethods& authMethodsConfig =
1269 persistent_data::SessionStore::getInstance()
1270 .getAuthMethodsConfig();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001271
Ed Tanous6c51eab2021-06-03 12:30:29 -07001272 asyncResp->res.jsonValue = {
1273 {"@odata.id", "/redfish/v1/AccountService"},
1274 {"@odata.type", "#AccountService."
1275 "v1_5_0.AccountService"},
1276 {"Id", "AccountService"},
1277 {"Name", "Account Service"},
1278 {"Description", "Account Service"},
1279 {"ServiceEnabled", true},
1280 {"MaxPasswordLength", 20},
1281 {"Accounts",
1282 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
1283 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
1284 {"Oem",
1285 {{"OpenBMC",
1286 {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
Ed Tanousd8e3c292021-09-21 18:01:43 -07001287 {"@odata.id", "/redfish/v1/AccountService#/Oem/OpenBMC"},
Ed Tanous6c51eab2021-06-03 12:30:29 -07001288 {"AuthMethods",
1289 {
1290 {"BasicAuth", authMethodsConfig.basic},
1291 {"SessionToken", authMethodsConfig.sessionToken},
1292 {"XToken", authMethodsConfig.xtoken},
1293 {"Cookie", authMethodsConfig.cookie},
1294 {"TLS", authMethodsConfig.tls},
Abhishek Patel72048782021-06-02 09:53:24 -05001295 }}}}}}};
1296 // /redfish/v1/AccountService/LDAP/Certificates is something only
1297 // ConfigureManager can access then only display when the user has
1298 // permissions ConfigureManager
1299 Privileges effectiveUserPrivileges =
1300 redfish::getUserPrivileges(req.userRole);
1301
1302 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1303 effectiveUserPrivileges))
1304 {
1305 asyncResp->res.jsonValue["LDAP"] = {
1306 {"Certificates",
1307 {{"@odata.id",
1308 "/redfish/v1/AccountService/LDAP/Certificates"}}}};
1309 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001310 crow::connections::systemBus->async_method_call(
1311 [asyncResp](
1312 const boost::system::error_code ec,
1313 const std::vector<
Ed Tanous168e20c2021-12-13 14:39:53 -08001314 std::pair<std::string, dbus::utility::DbusVariantType>>&
Ed Tanous6c51eab2021-06-03 12:30:29 -07001315 propertiesList) {
1316 if (ec)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001317 {
1318 messages::internalError(asyncResp->res);
1319 return;
1320 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001321 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1322 << "properties for AccountService";
1323 for (const std::pair<std::string,
Ed Tanous168e20c2021-12-13 14:39:53 -08001324 dbus::utility::DbusVariantType>&
1325 property : propertiesList)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001326 {
1327 if (property.first == "MinPasswordLength")
1328 {
1329 const uint8_t* value =
1330 std::get_if<uint8_t>(&property.second);
1331 if (value != nullptr)
Ratan Gupta24c85422019-01-30 19:41:24 +05301332 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001333 asyncResp->res.jsonValue["MinPasswordLength"] =
1334 *value;
Ratan Gupta24c85422019-01-30 19:41:24 +05301335 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001336 }
1337 if (property.first == "AccountUnlockTimeout")
1338 {
1339 const uint32_t* value =
1340 std::get_if<uint32_t>(&property.second);
1341 if (value != nullptr)
1342 {
1343 asyncResp->res
1344 .jsonValue["AccountLockoutDuration"] =
1345 *value;
1346 }
1347 }
1348 if (property.first == "MaxLoginAttemptBeforeLockout")
1349 {
1350 const uint16_t* value =
1351 std::get_if<uint16_t>(&property.second);
1352 if (value != nullptr)
1353 {
1354 asyncResp->res
1355 .jsonValue["AccountLockoutThreshold"] =
1356 *value;
1357 }
1358 }
1359 }
1360 },
1361 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1362 "org.freedesktop.DBus.Properties", "GetAll",
1363 "xyz.openbmc_project.User.AccountPolicy");
1364
1365 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
1366 const std::string& ldapType) {
1367 if (!success)
1368 {
1369 return;
1370 }
1371 parseLDAPConfigData(asyncResp->res.jsonValue, confData,
1372 ldapType);
1373 };
1374
1375 getLDAPConfigData("LDAP", callback);
1376 getLDAPConfigData("ActiveDirectory", callback);
1377 });
1378
Ed Tanousf5ffd802021-07-19 10:55:33 -07001379 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06001380 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07001381 .methods(boost::beast::http::verb::patch)(
1382 [](const crow::Request& req,
1383 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1384 std::optional<uint32_t> unlockTimeout;
1385 std::optional<uint16_t> lockoutThreshold;
Paul Fertseref73ad02022-01-21 19:44:40 +00001386 std::optional<uint8_t> minPasswordLength;
Ed Tanousf5ffd802021-07-19 10:55:33 -07001387 std::optional<uint16_t> maxPasswordLength;
1388 std::optional<nlohmann::json> ldapObject;
1389 std::optional<nlohmann::json> activeDirectoryObject;
1390 std::optional<nlohmann::json> oemObject;
1391
Willy Tu15ed6782021-12-14 11:03:16 -08001392 if (!json_util::readJsonPatch(
Ed Tanousf5ffd802021-07-19 10:55:33 -07001393 req, asyncResp->res, "AccountLockoutDuration",
1394 unlockTimeout, "AccountLockoutThreshold",
1395 lockoutThreshold, "MaxPasswordLength",
1396 maxPasswordLength, "MinPasswordLength",
1397 minPasswordLength, "LDAP", ldapObject,
1398 "ActiveDirectory", activeDirectoryObject, "Oem",
1399 oemObject))
1400 {
1401 return;
1402 }
1403
1404 if (minPasswordLength)
1405 {
Paul Fertseref73ad02022-01-21 19:44:40 +00001406 crow::connections::systemBus->async_method_call(
1407 [asyncResp](const boost::system::error_code ec) {
1408 if (ec)
1409 {
1410 messages::internalError(asyncResp->res);
1411 return;
1412 }
1413 messages::success(asyncResp->res);
1414 },
1415 "xyz.openbmc_project.User.Manager",
1416 "/xyz/openbmc_project/user",
1417 "org.freedesktop.DBus.Properties", "Set",
1418 "xyz.openbmc_project.User.AccountPolicy",
1419 "MinPasswordLength",
1420 dbus::utility::DbusVariantType(*minPasswordLength));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001421 }
1422
1423 if (maxPasswordLength)
1424 {
1425 messages::propertyNotWritable(asyncResp->res,
1426 "MaxPasswordLength");
1427 }
1428
1429 if (ldapObject)
1430 {
1431 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1432 }
1433
1434 if (std::optional<nlohmann::json> oemOpenBMCObject;
1435 oemObject &&
1436 json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1437 oemOpenBMCObject))
1438 {
1439 if (std::optional<nlohmann::json> authMethodsObject;
1440 oemOpenBMCObject &&
1441 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1442 "AuthMethods", authMethodsObject))
1443 {
1444 if (authMethodsObject)
1445 {
1446 handleAuthMethodsPatch(*authMethodsObject,
1447 asyncResp);
1448 }
1449 }
1450 }
1451
1452 if (activeDirectoryObject)
1453 {
1454 handleLDAPPatch(*activeDirectoryObject, asyncResp,
1455 "ActiveDirectory");
1456 }
1457
1458 if (unlockTimeout)
1459 {
1460 crow::connections::systemBus->async_method_call(
1461 [asyncResp](const boost::system::error_code ec) {
1462 if (ec)
1463 {
1464 messages::internalError(asyncResp->res);
1465 return;
1466 }
1467 messages::success(asyncResp->res);
1468 },
1469 "xyz.openbmc_project.User.Manager",
1470 "/xyz/openbmc_project/user",
1471 "org.freedesktop.DBus.Properties", "Set",
1472 "xyz.openbmc_project.User.AccountPolicy",
1473 "AccountUnlockTimeout",
Ed Tanous168e20c2021-12-13 14:39:53 -08001474 dbus::utility::DbusVariantType(*unlockTimeout));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001475 }
1476 if (lockoutThreshold)
1477 {
1478 crow::connections::systemBus->async_method_call(
1479 [asyncResp](const boost::system::error_code ec) {
1480 if (ec)
1481 {
1482 messages::internalError(asyncResp->res);
1483 return;
1484 }
1485 messages::success(asyncResp->res);
1486 },
1487 "xyz.openbmc_project.User.Manager",
1488 "/xyz/openbmc_project/user",
1489 "org.freedesktop.DBus.Properties", "Set",
1490 "xyz.openbmc_project.User.AccountPolicy",
1491 "MaxLoginAttemptBeforeLockout",
Ed Tanous168e20c2021-12-13 14:39:53 -08001492 dbus::utility::DbusVariantType(*lockoutThreshold));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001493 }
1494 });
1495
Ed Tanous6c51eab2021-06-03 12:30:29 -07001496 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001497 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001498 .methods(boost::beast::http::verb::get)(
1499 [](const crow::Request& req,
1500 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1501 asyncResp->res.jsonValue = {
1502 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
1503 {"@odata.type", "#ManagerAccountCollection."
1504 "ManagerAccountCollection"},
1505 {"Name", "Accounts Collection"},
1506 {"Description", "BMC User Accounts"}};
1507
Ed Tanous6c51eab2021-06-03 12:30:29 -07001508 Privileges effectiveUserPrivileges =
1509 redfish::getUserPrivileges(req.userRole);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001510
JunLin Chenf5e29f32021-12-08 16:47:04 +08001511 std::string thisUser;
1512 if (req.session)
1513 {
1514 thisUser = req.session->username;
1515 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001516 crow::connections::systemBus->async_method_call(
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001517 [asyncResp, thisUser, effectiveUserPrivileges](
1518 const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001519 const dbus::utility::ManagedObjectType& users) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001520 if (ec)
1521 {
1522 messages::internalError(asyncResp->res);
Ratan Gupta24c85422019-01-30 19:41:24 +05301523 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001524 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001525
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001526 bool userCanSeeAllAccounts =
1527 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001528 {"ConfigureUsers"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001529
1530 bool userCanSeeSelf =
1531 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001532 {"ConfigureSelf"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001533
Ed Tanous6c51eab2021-06-03 12:30:29 -07001534 nlohmann::json& memberArray =
1535 asyncResp->res.jsonValue["Members"];
1536 memberArray = nlohmann::json::array();
Ratan Gupta24c85422019-01-30 19:41:24 +05301537
Ed Tanous9eb808c2022-01-25 10:19:23 -08001538 for (const auto& userpath : users)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001539 {
1540 std::string user = userpath.first.filename();
1541 if (user.empty())
Ratan Gupta24c85422019-01-30 19:41:24 +05301542 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301543 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001544 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1545
Ratan Gupta24c85422019-01-30 19:41:24 +05301546 return;
1547 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301548
Ed Tanous6c51eab2021-06-03 12:30:29 -07001549 // As clarified by Redfish here:
1550 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1551 // Users without ConfigureUsers, only see their own
1552 // account. Users with ConfigureUsers, see all
1553 // accounts.
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001554 if (userCanSeeAllAccounts ||
1555 (thisUser == user && userCanSeeSelf))
Ratan Gupta24c85422019-01-30 19:41:24 +05301556 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001557 memberArray.push_back(
1558 {{"@odata.id",
1559 "/redfish/v1/AccountService/Accounts/" +
1560 user}});
Ratan Gupta24c85422019-01-30 19:41:24 +05301561 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001562 }
1563 asyncResp->res.jsonValue["Members@odata.count"] =
1564 memberArray.size();
1565 },
1566 "xyz.openbmc_project.User.Manager",
1567 "/xyz/openbmc_project/user",
1568 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Ratan Gupta24c85422019-01-30 19:41:24 +05301569 });
Ed Tanous06e086d2018-09-19 17:19:52 -07001570
Ed Tanous6c51eab2021-06-03 12:30:29 -07001571 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001572 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001573 .methods(boost::beast::http::verb::post)([](const crow::Request& req,
1574 const std::shared_ptr<
1575 bmcweb::AsyncResp>&
1576 asyncResp) -> void {
1577 std::string username;
1578 std::string password;
1579 std::optional<std::string> roleId("User");
1580 std::optional<bool> enabled = true;
Willy Tu15ed6782021-12-14 11:03:16 -08001581 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
1582 username, "Password", password,
1583 "RoleId", roleId, "Enabled", enabled))
Ed Tanous6c51eab2021-06-03 12:30:29 -07001584 {
1585 return;
1586 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001587
Ed Tanous6c51eab2021-06-03 12:30:29 -07001588 std::string priv = getPrivilegeFromRoleId(*roleId);
1589 if (priv.empty())
1590 {
1591 messages::propertyValueNotInList(asyncResp->res, *roleId,
1592 "RoleId");
1593 return;
1594 }
1595 // TODO: Following override will be reverted once support in
1596 // phosphor-user-manager is added. In order to avoid dependency
1597 // issues, this is added in bmcweb, which will removed, once
1598 // phosphor-user-manager supports priv-noaccess.
1599 if (priv == "priv-noaccess")
1600 {
1601 roleId = "";
1602 }
1603 else
1604 {
1605 roleId = priv;
1606 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001607
Ed Tanous6c51eab2021-06-03 12:30:29 -07001608 // Reading AllGroups property
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001609 sdbusplus::asio::getProperty<std::vector<std::string>>(
1610 *crow::connections::systemBus,
1611 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1612 "xyz.openbmc_project.User.Manager", "AllGroups",
Ed Tanous6c51eab2021-06-03 12:30:29 -07001613 [asyncResp, username, password{std::move(password)}, roleId,
Ed Tanous168e20c2021-12-13 14:39:53 -08001614 enabled](const boost::system::error_code ec,
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001615 const std::vector<std::string>& allGroupsList) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001616 if (ec)
1617 {
1618 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1619 messages::internalError(asyncResp->res);
1620 return;
1621 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001622
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001623 if (allGroupsList.empty())
Ed Tanous6c51eab2021-06-03 12:30:29 -07001624 {
1625 messages::internalError(asyncResp->res);
1626 return;
1627 }
1628
1629 crow::connections::systemBus->async_method_call(
1630 [asyncResp, username,
1631 password](const boost::system::error_code ec2,
1632 sdbusplus::message::message& m) {
1633 if (ec2)
1634 {
1635 userErrorMessageHandler(
1636 m.get_error(), asyncResp, username, "");
1637 return;
1638 }
1639
1640 if (pamUpdatePassword(username, password) !=
1641 PAM_SUCCESS)
1642 {
1643 // At this point we have a user that's been
1644 // created, but the password set
1645 // failed.Something is wrong, so delete the user
1646 // that we've already created
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301647 sdbusplus::message::object_path tempObjPath(
1648 rootUserDbusPath);
1649 tempObjPath /= username;
1650 const std::string userPath(tempObjPath);
1651
Ed Tanous6c51eab2021-06-03 12:30:29 -07001652 crow::connections::systemBus->async_method_call(
1653 [asyncResp, password](
1654 const boost::system::error_code ec3) {
1655 if (ec3)
1656 {
1657 messages::internalError(
1658 asyncResp->res);
1659 return;
1660 }
1661
1662 // If password is invalid
1663 messages::propertyValueFormatError(
1664 asyncResp->res, password,
1665 "Password");
1666 },
1667 "xyz.openbmc_project.User.Manager",
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301668 userPath,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001669 "xyz.openbmc_project.Object.Delete",
1670 "Delete");
1671
1672 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1673 return;
1674 }
1675
1676 messages::created(asyncResp->res);
1677 asyncResp->res.addHeader(
1678 "Location",
1679 "/redfish/v1/AccountService/Accounts/" +
1680 username);
1681 },
1682 "xyz.openbmc_project.User.Manager",
1683 "/xyz/openbmc_project/user",
1684 "xyz.openbmc_project.User.Manager", "CreateUser",
Jonathan Doman1e1e5982021-06-11 09:36:17 -07001685 username, allGroupsList, *roleId, *enabled);
1686 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001687 });
1688
1689 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001690 .privileges(redfish::privileges::getManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001691 .methods(
1692 boost::beast::http::verb::
1693 get)([](const crow::Request& req,
1694 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1695 const std::string& accountName) -> void {
1696 if (req.session->username != accountName)
1697 {
1698 // At this point we've determined that the user is trying to
1699 // modify a user that isn't them. We need to verify that they
1700 // have permissions to modify other users, so re-run the auth
1701 // check with the same permissions, minus ConfigureSelf.
1702 Privileges effectiveUserPrivileges =
1703 redfish::getUserPrivileges(req.userRole);
1704 Privileges requiredPermissionsToChangeNonSelf = {
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001705 "ConfigureUsers", "ConfigureManager"};
Ed Tanous6c51eab2021-06-03 12:30:29 -07001706 if (!effectiveUserPrivileges.isSupersetOf(
1707 requiredPermissionsToChangeNonSelf))
Ed Tanous06e086d2018-09-19 17:19:52 -07001708 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001709 BMCWEB_LOG_DEBUG << "GET Account denied access";
1710 messages::insufficientPrivilege(asyncResp->res);
1711 return;
1712 }
1713 }
1714
1715 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -08001716 [asyncResp,
1717 accountName](const boost::system::error_code ec,
1718 const dbus::utility::ManagedObjectType& users) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001719 if (ec)
1720 {
1721 messages::internalError(asyncResp->res);
1722 return;
1723 }
Ed Tanous711ac7a2021-12-20 09:34:41 -08001724 const auto userIt = std::find_if(
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301725 users.begin(), users.end(),
1726 [accountName](
1727 const std::pair<sdbusplus::message::object_path,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001728 dbus::utility::DBusInteracesMap>&
1729 user) {
Ed Tanous55f79e62022-01-25 11:26:16 -08001730 return accountName == user.first.filename();
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301731 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001732
Ed Tanous6c51eab2021-06-03 12:30:29 -07001733 if (userIt == users.end())
1734 {
1735 messages::resourceNotFound(
1736 asyncResp->res, "ManagerAccount", accountName);
1737 return;
1738 }
1739
1740 asyncResp->res.jsonValue = {
1741 {"@odata.type",
1742 "#ManagerAccount.v1_4_0.ManagerAccount"},
1743 {"Name", "User Account"},
1744 {"Description", "User Account"},
1745 {"Password", nullptr},
1746 {"AccountTypes", {"Redfish"}}};
1747
1748 for (const auto& interface : userIt->second)
1749 {
1750 if (interface.first ==
1751 "xyz.openbmc_project.User.Attributes")
1752 {
1753 for (const auto& property : interface.second)
1754 {
1755 if (property.first == "UserEnabled")
1756 {
1757 const bool* userEnabled =
1758 std::get_if<bool>(&property.second);
1759 if (userEnabled == nullptr)
1760 {
1761 BMCWEB_LOG_ERROR
1762 << "UserEnabled wasn't a bool";
1763 messages::internalError(asyncResp->res);
1764 return;
1765 }
1766 asyncResp->res.jsonValue["Enabled"] =
1767 *userEnabled;
1768 }
1769 else if (property.first ==
1770 "UserLockedForFailedAttempt")
1771 {
1772 const bool* userLocked =
1773 std::get_if<bool>(&property.second);
1774 if (userLocked == nullptr)
1775 {
1776 BMCWEB_LOG_ERROR << "UserLockedForF"
1777 "ailedAttempt "
1778 "wasn't a bool";
1779 messages::internalError(asyncResp->res);
1780 return;
1781 }
1782 asyncResp->res.jsonValue["Locked"] =
1783 *userLocked;
1784 asyncResp->res.jsonValue
1785 ["Locked@Redfish.AllowableValues"] = {
1786 "false"}; // can only unlock accounts
1787 }
1788 else if (property.first == "UserPrivilege")
1789 {
1790 const std::string* userPrivPtr =
1791 std::get_if<std::string>(
1792 &property.second);
1793 if (userPrivPtr == nullptr)
1794 {
1795 BMCWEB_LOG_ERROR
1796 << "UserPrivilege wasn't a "
1797 "string";
1798 messages::internalError(asyncResp->res);
1799 return;
1800 }
1801 std::string role =
1802 getRoleIdFromPrivilege(*userPrivPtr);
1803 if (role.empty())
1804 {
1805 BMCWEB_LOG_ERROR << "Invalid user role";
1806 messages::internalError(asyncResp->res);
1807 return;
1808 }
1809 asyncResp->res.jsonValue["RoleId"] = role;
1810
1811 asyncResp->res.jsonValue["Links"]["Role"] =
1812 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +08001813 "/redfish/v1/AccountService/Roles/" +
Ed Tanous6c51eab2021-06-03 12:30:29 -07001814 role}};
1815 }
1816 else if (property.first ==
1817 "UserPasswordExpired")
1818 {
1819 const bool* userPasswordExpired =
1820 std::get_if<bool>(&property.second);
1821 if (userPasswordExpired == nullptr)
1822 {
George Liu0fda0f12021-11-16 10:06:17 +08001823 BMCWEB_LOG_ERROR
1824 << "UserPasswordExpired wasn't a bool";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001825 messages::internalError(asyncResp->res);
1826 return;
1827 }
1828 asyncResp->res
1829 .jsonValue["PasswordChangeRequired"] =
1830 *userPasswordExpired;
1831 }
1832 }
1833 }
1834 }
1835
1836 asyncResp->res.jsonValue["@odata.id"] =
1837 "/redfish/v1/AccountService/Accounts/" + accountName;
1838 asyncResp->res.jsonValue["Id"] = accountName;
1839 asyncResp->res.jsonValue["UserName"] = accountName;
1840 },
1841 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1842 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1843 });
1844
1845 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001846 // TODO this privilege should be using the generated endpoints, but
1847 // because of the special handling of ConfigureSelf, it's not able to
1848 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001849 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1850 .methods(boost::beast::http::verb::patch)(
1851 [](const crow::Request& req,
1852 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1853 const std::string& username) -> void {
1854 std::optional<std::string> newUserName;
1855 std::optional<std::string> password;
1856 std::optional<bool> enabled;
1857 std::optional<std::string> roleId;
1858 std::optional<bool> locked;
Ed Tanouse9cc5172021-11-03 14:13:19 +08001859
1860 Privileges effectiveUserPrivileges =
1861 redfish::getUserPrivileges(req.userRole);
1862 Privileges configureUsers = {"ConfigureUsers"};
1863 bool userHasConfigureUsers =
1864 effectiveUserPrivileges.isSupersetOf(configureUsers);
1865 if (userHasConfigureUsers)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001866 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001867 // Users with ConfigureUsers can modify for all users
Willy Tu15ed6782021-12-14 11:03:16 -08001868 if (!json_util::readJsonPatch(
1869 req, asyncResp->res, "UserName", newUserName,
1870 "Password", password, "RoleId", roleId, "Enabled",
1871 enabled, "Locked", locked))
Ed Tanouse9cc5172021-11-03 14:13:19 +08001872 {
1873 return;
1874 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001875 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001876 else
Ed Tanous6c51eab2021-06-03 12:30:29 -07001877 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001878 // ConfigureSelf accounts can only modify their own account
1879 if (username != req.session->username)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001880 {
1881 messages::insufficientPrivilege(asyncResp->res);
1882 return;
1883 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001884 // ConfigureSelf accounts can only modify their password
Willy Tu15ed6782021-12-14 11:03:16 -08001885 if (!json_util::readJsonPatch(req, asyncResp->res,
1886 "Password", password))
Ed Tanouse9cc5172021-11-03 14:13:19 +08001887 {
1888 return;
1889 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001890 }
1891
1892 // if user name is not provided in the patch method or if it
1893 // matches the user name in the URI, then we are treating it as
1894 // updating user properties other then username. If username
1895 // provided doesn't match the URI, then we are treating this as
1896 // user rename request.
1897 if (!newUserName || (newUserName.value() == username))
1898 {
1899 updateUserProperties(asyncResp, username, password, enabled,
1900 roleId, locked);
1901 return;
1902 }
1903 crow::connections::systemBus->async_method_call(
1904 [asyncResp, username, password(std::move(password)),
1905 roleId(std::move(roleId)), enabled,
1906 newUser{std::string(*newUserName)},
1907 locked](const boost::system::error_code ec,
1908 sdbusplus::message::message& m) {
1909 if (ec)
1910 {
1911 userErrorMessageHandler(m.get_error(), asyncResp,
1912 newUser, username);
1913 return;
1914 }
1915
1916 updateUserProperties(asyncResp, newUser, password,
1917 enabled, roleId, locked);
1918 },
1919 "xyz.openbmc_project.User.Manager",
1920 "/xyz/openbmc_project/user",
1921 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1922 *newUserName);
1923 });
1924
1925 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001926 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001927 .methods(boost::beast::http::verb::delete_)(
1928 [](const crow::Request& /*req*/,
1929 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1930 const std::string& username) -> void {
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301931 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1932 tempObjPath /= username;
1933 const std::string userPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001934
1935 crow::connections::systemBus->async_method_call(
1936 [asyncResp, username](const boost::system::error_code ec) {
1937 if (ec)
1938 {
1939 messages::resourceNotFound(
1940 asyncResp->res,
1941 "#ManagerAccount.v1_4_0.ManagerAccount",
1942 username);
1943 return;
1944 }
1945
1946 messages::accountRemoved(asyncResp->res);
1947 },
1948 "xyz.openbmc_project.User.Manager", userPath,
1949 "xyz.openbmc_project.Object.Delete", "Delete");
1950 });
1951}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001952
Ed Tanous1abe55e2018-09-05 08:30:59 -07001953} // namespace redfish