blob: c8fd196af35315d3b5c1af27abc7e29eb8fcb386 [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
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080018#include "app.hpp"
19#include "dbus_utility.hpp"
20#include "error_messages.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070021#include "generated/enums/account_service.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080022#include "openbmc_dbus_rest.hpp"
23#include "persistent_data.hpp"
24#include "query.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070025#include "registries/privilege_registry.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080026#include "utils/dbus_utils.hpp"
27#include "utils/json_utils.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070028
Jonathan Doman1e1e5982021-06-11 09:36:17 -070029#include <sdbusplus/asio/property.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020030#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050031
George Liu2b731192023-01-11 16:27:13 +080032#include <array>
Abhishek Patelc7229812022-02-01 10:07:15 -060033#include <optional>
34#include <string>
George Liu2b731192023-01-11 16:27:13 +080035#include <string_view>
Abhishek Patelc7229812022-02-01 10:07:15 -060036#include <vector>
37
Ed Tanous1abe55e2018-09-05 08:30:59 -070038namespace redfish
39{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010040
Ed Tanous23a21a12020-07-25 04:45:05 +000041constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053042 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070043constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053044 "/xyz/openbmc_project/user/ldap/active_directory";
45
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053046constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053047constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
48constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
49constexpr const char* ldapConfigInterface =
50 "xyz.openbmc_project.User.Ldap.Config";
51constexpr const char* ldapCreateInterface =
52 "xyz.openbmc_project.User.Ldap.Create";
53constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053054constexpr const char* ldapPrivMapperInterface =
55 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053056constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
57constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
Ratan Gupta6973a582018-12-13 18:25:44 +053058
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060059struct LDAPRoleMapData
60{
61 std::string groupName;
62 std::string privilege;
63};
64
Ratan Gupta6973a582018-12-13 18:25:44 +053065struct LDAPConfigData
66{
67 std::string uri{};
68 std::string bindDN{};
69 std::string baseDN{};
70 std::string searchScope{};
71 std::string serverType{};
72 bool serviceEnabled = false;
73 std::string userNameAttribute{};
74 std::string groupAttribute{};
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060075 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053076};
77
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060078inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053079{
80 if (role == "priv-admin")
81 {
82 return "Administrator";
83 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070084 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053085 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053086 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053087 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070088 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053089 {
90 return "Operator";
91 }
92 return "";
93}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060094inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053095{
96 if (role == "Administrator")
97 {
98 return "priv-admin";
99 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700100 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530101 {
102 return "priv-user";
103 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700104 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530105 {
106 return "priv-operator";
107 }
108 return "";
109}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700110
Abhishek Patelc7229812022-02-01 10:07:15 -0600111/**
112 * @brief Maps user group names retrieved from D-Bus object to
113 * Account Types.
114 *
115 * @param[in] userGroups List of User groups
116 * @param[out] res AccountTypes populated
117 *
118 * @return true in case of success, false if UserGroups contains
119 * invalid group name(s).
120 */
121inline bool translateUserGroup(const std::vector<std::string>& userGroups,
122 crow::Response& res)
123{
124 std::vector<std::string> accountTypes;
125 for (const auto& userGroup : userGroups)
126 {
127 if (userGroup == "redfish")
128 {
129 accountTypes.emplace_back("Redfish");
130 accountTypes.emplace_back("WebUI");
131 }
132 else if (userGroup == "ipmi")
133 {
134 accountTypes.emplace_back("IPMI");
135 }
136 else if (userGroup == "ssh")
137 {
Abhishek Patelc7229812022-02-01 10:07:15 -0600138 accountTypes.emplace_back("ManagerConsole");
139 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500140 else if (userGroup == "hostconsole")
141 {
142 // The hostconsole group controls who can access the host console
143 // port via ssh and websocket.
144 accountTypes.emplace_back("HostConsole");
145 }
Abhishek Patelc7229812022-02-01 10:07:15 -0600146 else if (userGroup == "web")
147 {
148 // 'web' is one of the valid groups in the UserGroups property of
149 // the user account in the D-Bus object. This group is currently not
150 // doing anything, and is considered to be equivalent to 'redfish'.
151 // 'redfish' user group is mapped to 'Redfish'and 'WebUI'
152 // AccountTypes, so do nothing here...
153 }
154 else
155 {
156 // Invalid user group name. Caller throws an excption.
157 return false;
158 }
159 }
160
161 res.jsonValue["AccountTypes"] = std::move(accountTypes);
162 return true;
163}
164
zhanghch058d1b46d2021-04-01 11:18:24 +0800165inline void userErrorMessageHandler(
166 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
167 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000168{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000169 if (e == nullptr)
170 {
171 messages::internalError(asyncResp->res);
172 return;
173 }
174
Manojkiran Eda055806b2020-11-03 09:36:28 +0530175 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000176 if (strcmp(errorMessage,
177 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
178 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800179 messages::resourceAlreadyExists(asyncResp->res, "ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000180 "UserName", newUser);
181 }
182 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
183 "UserNameDoesNotExist") == 0)
184 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800185 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000186 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700187 else if ((strcmp(errorMessage,
188 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
189 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800190 (strcmp(
191 errorMessage,
192 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
193 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000194 {
195 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
196 }
197 else if (strcmp(errorMessage,
198 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
199 {
200 messages::createLimitReachedForResource(asyncResp->res);
201 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000202 else
203 {
204 messages::internalError(asyncResp->res);
205 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000206}
207
Ed Tanous81ce6092020-12-17 16:54:55 +0000208inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000209 const LDAPConfigData& confData,
210 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530211{
Patrick Williams89492a12023-05-10 07:51:34 -0500212 std::string service = (ldapType == "LDAP") ? "LDAPService"
213 : "ActiveDirectoryService";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600214
Ed Tanous14766872022-03-15 10:44:42 -0700215 nlohmann::json& ldap = jsonResponse[ldapType];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600216
Ed Tanous14766872022-03-15 10:44:42 -0700217 ldap["ServiceEnabled"] = confData.serviceEnabled;
218 ldap["ServiceAddresses"] = nlohmann::json::array({confData.uri});
Ed Tanous0ec8b832022-03-14 14:56:47 -0700219 ldap["Authentication"]["AuthenticationType"] =
220 account_service::AuthenticationTypes::UsernameAndPassword;
Ed Tanous14766872022-03-15 10:44:42 -0700221 ldap["Authentication"]["Username"] = confData.bindDN;
222 ldap["Authentication"]["Password"] = nullptr;
223
224 ldap["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"] =
225 nlohmann::json::array({confData.baseDN});
226 ldap["LDAPService"]["SearchSettings"]["UsernameAttribute"] =
227 confData.userNameAttribute;
228 ldap["LDAPService"]["SearchSettings"]["GroupsAttribute"] =
229 confData.groupAttribute;
230
231 nlohmann::json& roleMapArray = ldap["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600232 roleMapArray = nlohmann::json::array();
Ed Tanous9eb808c2022-01-25 10:19:23 -0800233 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600234 {
235 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
236 << obj.second.groupName << "\n";
Ed Tanous613dabe2022-07-09 11:17:36 -0700237
Ed Tanous613dabe2022-07-09 11:17:36 -0700238 nlohmann::json::object_t remoteGroup;
239 remoteGroup["RemoteGroup"] = obj.second.groupName;
Jorge Cisneros329f0342022-11-04 16:26:25 +0000240 remoteGroup["LocalRole"] = getRoleIdFromPrivilege(obj.second.privilege);
241 roleMapArray.emplace_back(std::move(remoteGroup));
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600242 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530243}
244
245/**
Ratan Gupta06785242019-07-26 22:30:16 +0530246 * @brief validates given JSON input and then calls appropriate method to
247 * create, to delete or to set Rolemapping object based on the given input.
248 *
249 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000250inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800251 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530252 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700253 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530254{
255 for (size_t index = 0; index < input.size(); index++)
256 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700257 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530258
259 if (thisJson.is_null())
260 {
261 // delete the existing object
262 if (index < roleMapObjData.size())
263 {
264 crow::connections::systemBus->async_method_call(
265 [asyncResp, roleMapObjData, serverType,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800266 index](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700267 if (ec)
268 {
269 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
270 messages::internalError(asyncResp->res);
271 return;
272 }
Patrick Williams89492a12023-05-10 07:51:34 -0500273 asyncResp->res.jsonValue[serverType]["RemoteRoleMapping"]
274 [index] = nullptr;
Ratan Gupta06785242019-07-26 22:30:16 +0530275 },
276 ldapDbusService, roleMapObjData[index].first,
277 "xyz.openbmc_project.Object.Delete", "Delete");
278 }
279 else
280 {
281 BMCWEB_LOG_ERROR << "Can't delete the object";
282 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800283 asyncResp->res,
284 thisJson.dump(2, ' ', true,
285 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530286 "RemoteRoleMapping/" + std::to_string(index));
287 return;
288 }
289 }
290 else if (thisJson.empty())
291 {
292 // Don't do anything for the empty objects,parse next json
293 // eg {"RemoteRoleMapping",[{}]}
294 }
295 else
296 {
297 // update/create the object
298 std::optional<std::string> remoteGroup;
299 std::optional<std::string> localRole;
300
Ed Tanousf23b7292020-10-15 09:41:17 -0700301 // This is a copy, but it's required in this case because of how
302 // readJson is structured
303 nlohmann::json thisJsonCopy = thisJson;
304 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
305 "RemoteGroup", remoteGroup, "LocalRole",
306 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530307 {
308 continue;
309 }
310
311 // Update existing RoleMapping Object
312 if (index < roleMapObjData.size())
313 {
314 BMCWEB_LOG_DEBUG << "Update Role Map Object";
315 // If "RemoteGroup" info is provided
316 if (remoteGroup)
317 {
318 crow::connections::systemBus->async_method_call(
319 [asyncResp, roleMapObjData, serverType, index,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800320 remoteGroup](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700321 if (ec)
322 {
323 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
324 messages::internalError(asyncResp->res);
325 return;
326 }
327 asyncResp->res
328 .jsonValue[serverType]["RemoteRoleMapping"][index]
329 ["RemoteGroup"] = *remoteGroup;
Ratan Gupta06785242019-07-26 22:30:16 +0530330 },
331 ldapDbusService, roleMapObjData[index].first,
332 propertyInterface, "Set",
333 "xyz.openbmc_project.User.PrivilegeMapperEntry",
334 "GroupName",
Ed Tanous168e20c2021-12-13 14:39:53 -0800335 dbus::utility::DbusVariantType(
336 std::move(*remoteGroup)));
Ratan Gupta06785242019-07-26 22:30:16 +0530337 }
338
339 // If "LocalRole" info is provided
340 if (localRole)
341 {
342 crow::connections::systemBus->async_method_call(
343 [asyncResp, roleMapObjData, serverType, index,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800344 localRole](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700345 if (ec)
346 {
347 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
348 messages::internalError(asyncResp->res);
349 return;
350 }
351 asyncResp->res
352 .jsonValue[serverType]["RemoteRoleMapping"][index]
353 ["LocalRole"] = *localRole;
Ratan Gupta06785242019-07-26 22:30:16 +0530354 },
355 ldapDbusService, roleMapObjData[index].first,
356 propertyInterface, "Set",
357 "xyz.openbmc_project.User.PrivilegeMapperEntry",
358 "Privilege",
Ed Tanous168e20c2021-12-13 14:39:53 -0800359 dbus::utility::DbusVariantType(
Ratan Gupta06785242019-07-26 22:30:16 +0530360 getPrivilegeFromRoleId(std::move(*localRole))));
361 }
362 }
363 // Create a new RoleMapping Object.
364 else
365 {
366 BMCWEB_LOG_DEBUG
367 << "setRoleMappingProperties: Creating new Object";
Patrick Williams89492a12023-05-10 07:51:34 -0500368 std::string pathString = "RemoteRoleMapping/" +
369 std::to_string(index);
Ratan Gupta06785242019-07-26 22:30:16 +0530370
371 if (!localRole)
372 {
373 messages::propertyMissing(asyncResp->res,
374 pathString + "/LocalRole");
375 continue;
376 }
377 if (!remoteGroup)
378 {
379 messages::propertyMissing(asyncResp->res,
380 pathString + "/RemoteGroup");
381 continue;
382 }
383
384 std::string dbusObjectPath;
385 if (serverType == "ActiveDirectory")
386 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700387 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530388 }
389 else if (serverType == "LDAP")
390 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000391 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530392 }
393
394 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
395 << ",LocalRole=" << *localRole;
396
397 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700398 [asyncResp, serverType, localRole,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800399 remoteGroup](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700400 if (ec)
401 {
402 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
403 messages::internalError(asyncResp->res);
404 return;
405 }
406 nlohmann::json& remoteRoleJson =
407 asyncResp->res
408 .jsonValue[serverType]["RemoteRoleMapping"];
409 nlohmann::json::object_t roleMapEntry;
410 roleMapEntry["LocalRole"] = *localRole;
411 roleMapEntry["RemoteGroup"] = *remoteGroup;
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500412 remoteRoleJson.emplace_back(std::move(roleMapEntry));
Ratan Gupta06785242019-07-26 22:30:16 +0530413 },
414 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700415 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530416 getPrivilegeFromRoleId(std::move(*localRole)));
417 }
418 }
419 }
420}
421
422/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530423 * Function that retrieves all properties for LDAP config object
424 * into JSON
425 */
426template <typename CallbackFunc>
427inline void getLDAPConfigData(const std::string& ldapType,
428 CallbackFunc&& callback)
429{
George Liu2b731192023-01-11 16:27:13 +0800430 constexpr std::array<std::string_view, 2> interfaces = {
431 ldapEnableInterface, ldapConfigInterface};
Ratan Gupta6973a582018-12-13 18:25:44 +0530432
George Liu2b731192023-01-11 16:27:13 +0800433 dbus::utility::getDbusObject(
434 ldapConfigObjectName, interfaces,
435 [callback, ldapType](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800436 const dbus::utility::MapperGetObject& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700437 if (ec || resp.empty())
438 {
439 BMCWEB_LOG_ERROR
440 << "DBUS response error during getting of service name: " << ec;
441 LDAPConfigData empty{};
442 callback(false, empty, ldapType);
443 return;
444 }
445 std::string service = resp.begin()->first;
446 crow::connections::systemBus->async_method_call(
447 [callback,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800448 ldapType](const boost::system::error_code& errorCode,
Ed Tanous002d39b2022-05-31 08:59:27 -0700449 const dbus::utility::ManagedObjectType& ldapObjects) {
450 LDAPConfigData confData{};
451 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600452 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700453 callback(false, confData, ldapType);
454 BMCWEB_LOG_ERROR << "D-Bus responses error: " << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600455 return;
456 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600457
Ed Tanous002d39b2022-05-31 08:59:27 -0700458 std::string ldapDbusType;
459 std::string searchString;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600460
Ed Tanous002d39b2022-05-31 08:59:27 -0700461 if (ldapType == "LDAP")
462 {
463 ldapDbusType =
464 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
465 searchString = "openldap";
466 }
467 else if (ldapType == "ActiveDirectory")
468 {
469 ldapDbusType =
470 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
471 searchString = "active_directory";
472 }
473 else
474 {
475 BMCWEB_LOG_ERROR << "Can't get the DbusType for the given type="
476 << ldapType;
477 callback(false, confData, ldapType);
478 return;
479 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600480
Ed Tanous002d39b2022-05-31 08:59:27 -0700481 std::string ldapEnableInterfaceStr = ldapEnableInterface;
482 std::string ldapConfigInterfaceStr = ldapConfigInterface;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600483
Ed Tanous002d39b2022-05-31 08:59:27 -0700484 for (const auto& object : ldapObjects)
485 {
486 // let's find the object whose ldap type is equal to the
487 // given type
488 if (object.first.str.find(searchString) == std::string::npos)
489 {
490 continue;
491 }
492
493 for (const auto& interface : object.second)
494 {
495 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600496 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700497 // rest of the properties are string.
498 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600499 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700500 if (property.first == "Enabled")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600501 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700502 const bool* value =
503 std::get_if<bool>(&property.second);
504 if (value == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600505 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700506 continue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600507 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700508 confData.serviceEnabled = *value;
509 break;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600510 }
511 }
512 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700513 else if (interface.first == ldapConfigInterfaceStr)
514 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700515 for (const auto& property : interface.second)
516 {
517 const std::string* strValue =
518 std::get_if<std::string>(&property.second);
519 if (strValue == nullptr)
520 {
521 continue;
522 }
523 if (property.first == "LDAPServerURI")
524 {
525 confData.uri = *strValue;
526 }
527 else if (property.first == "LDAPBindDN")
528 {
529 confData.bindDN = *strValue;
530 }
531 else if (property.first == "LDAPBaseDN")
532 {
533 confData.baseDN = *strValue;
534 }
535 else if (property.first == "LDAPSearchScope")
536 {
537 confData.searchScope = *strValue;
538 }
539 else if (property.first == "GroupNameAttribute")
540 {
541 confData.groupAttribute = *strValue;
542 }
543 else if (property.first == "UserNameAttribute")
544 {
545 confData.userNameAttribute = *strValue;
546 }
547 else if (property.first == "LDAPType")
548 {
549 confData.serverType = *strValue;
550 }
551 }
552 }
553 else if (interface.first ==
554 "xyz.openbmc_project.User.PrivilegeMapperEntry")
555 {
556 LDAPRoleMapData roleMapData{};
557 for (const auto& property : interface.second)
558 {
559 const std::string* strValue =
560 std::get_if<std::string>(&property.second);
561
562 if (strValue == nullptr)
563 {
564 continue;
565 }
566
567 if (property.first == "GroupName")
568 {
569 roleMapData.groupName = *strValue;
570 }
571 else if (property.first == "Privilege")
572 {
573 roleMapData.privilege = *strValue;
574 }
575 }
576
577 confData.groupRoleList.emplace_back(object.first.str,
578 roleMapData);
579 }
580 }
581 }
582 callback(true, confData, ldapType);
583 },
584 service, ldapRootObject, dbusObjManagerIntf, "GetManagedObjects");
George Liu2b731192023-01-11 16:27:13 +0800585 });
Ratan Gupta6973a582018-12-13 18:25:44 +0530586}
587
Ed Tanous6c51eab2021-06-03 12:30:29 -0700588/**
589 * @brief parses the authentication section under the LDAP
590 * @param input JSON data
591 * @param asyncResp pointer to the JSON response
592 * @param userName userName to be filled from the given JSON.
593 * @param password password to be filled from the given JSON.
594 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700595inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700596 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
597 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700598{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700599 std::optional<std::string> authType;
600
601 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
602 authType, "Username", username, "Password",
603 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700604 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700605 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700606 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700607 if (!authType)
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 (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530612 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700613 messages::propertyValueNotInList(asyncResp->res, *authType,
614 "AuthenticationType");
615 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530616 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700617}
618/**
619 * @brief parses the LDAPService section under the LDAP
620 * @param input JSON data
621 * @param asyncResp pointer to the JSON response
622 * @param baseDNList baseDN to be filled from the given JSON.
623 * @param userNameAttribute userName to be filled from the given JSON.
624 * @param groupaAttribute password to be filled from the given JSON.
625 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530626
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700627inline void
628 parseLDAPServiceJson(nlohmann::json input,
629 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
630 std::optional<std::vector<std::string>>& baseDNList,
631 std::optional<std::string>& userNameAttribute,
632 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700633{
634 std::optional<nlohmann::json> searchSettings;
635
636 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
637 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530638 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700639 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530640 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700641 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530642 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700643 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530644 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700645 if (!json_util::readJson(*searchSettings, asyncResp->res,
646 "BaseDistinguishedNames", baseDNList,
647 "UsernameAttribute", userNameAttribute,
648 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530649 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700650 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530651 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700652}
653/**
654 * @brief updates the LDAP server address and updates the
655 json response with the new value.
656 * @param serviceAddressList address to be updated.
657 * @param asyncResp pointer to the JSON response
658 * @param ldapServerElementName Type of LDAP
659 server(openLDAP/ActiveDirectory)
660 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530661
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700662inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700663 const std::vector<std::string>& serviceAddressList,
664 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
665 const std::string& ldapServerElementName,
666 const std::string& ldapConfigObject)
667{
668 crow::connections::systemBus->async_method_call(
669 [asyncResp, ldapServerElementName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800670 serviceAddressList](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700671 if (ec)
672 {
673 BMCWEB_LOG_DEBUG
674 << "Error Occurred in updating the service address";
675 messages::internalError(asyncResp->res);
676 return;
677 }
678 std::vector<std::string> modifiedserviceAddressList = {
679 serviceAddressList.front()};
680 asyncResp->res.jsonValue[ldapServerElementName]["ServiceAddresses"] =
681 modifiedserviceAddressList;
682 if ((serviceAddressList).size() > 1)
683 {
684 messages::propertyValueModified(asyncResp->res, "ServiceAddresses",
685 serviceAddressList.front());
686 }
687 BMCWEB_LOG_DEBUG << "Updated the service address";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700688 },
689 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
690 ldapConfigInterface, "LDAPServerURI",
Ed Tanous168e20c2021-12-13 14:39:53 -0800691 dbus::utility::DbusVariantType(serviceAddressList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700692}
693/**
694 * @brief updates the LDAP Bind DN and updates the
695 json response with the new value.
696 * @param username name of the user which needs to be updated.
697 * @param asyncResp pointer to the JSON response
698 * @param ldapServerElementName Type of LDAP
699 server(openLDAP/ActiveDirectory)
700 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530701
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700702inline void
703 handleUserNamePatch(const std::string& username,
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, username,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800710 ldapServerElementName](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700711 if (ec)
712 {
713 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
714 messages::internalError(asyncResp->res);
715 return;
716 }
Patrick Williams89492a12023-05-10 07:51:34 -0500717 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
718 ["Username"] = username;
Ed Tanous002d39b2022-05-31 08:59:27 -0700719 BMCWEB_LOG_DEBUG << "Updated the username";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700720 },
721 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800722 ldapConfigInterface, "LDAPBindDN",
723 dbus::utility::DbusVariantType(username));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700724}
725
726/**
727 * @brief updates the LDAP password
728 * @param password : ldap password which needs to be updated.
729 * @param asyncResp pointer to the JSON response
730 * @param ldapServerElementName Type of LDAP
731 * server(openLDAP/ActiveDirectory)
732 */
733
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700734inline void
735 handlePasswordPatch(const std::string& password,
736 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
737 const std::string& ldapServerElementName,
738 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700739{
740 crow::connections::systemBus->async_method_call(
741 [asyncResp, password,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800742 ldapServerElementName](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700743 if (ec)
744 {
745 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
746 messages::internalError(asyncResp->res);
747 return;
748 }
Patrick Williams89492a12023-05-10 07:51:34 -0500749 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
750 ["Password"] = "";
Ed Tanous002d39b2022-05-31 08:59:27 -0700751 BMCWEB_LOG_DEBUG << "Updated the password";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700752 },
753 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
754 ldapConfigInterface, "LDAPBindDNPassword",
Ed Tanous168e20c2021-12-13 14:39:53 -0800755 dbus::utility::DbusVariantType(password));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700756}
757
758/**
759 * @brief updates the LDAP BaseDN and updates the
760 json response with the new value.
761 * @param baseDNList baseDN list which needs to be updated.
762 * @param asyncResp pointer to the JSON response
763 * @param ldapServerElementName Type of LDAP
764 server(openLDAP/ActiveDirectory)
765 */
766
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700767inline void
768 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
769 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
770 const std::string& ldapServerElementName,
771 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700772{
773 crow::connections::systemBus->async_method_call(
774 [asyncResp, baseDNList,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800775 ldapServerElementName](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700776 if (ec)
777 {
778 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
779 messages::internalError(asyncResp->res);
780 return;
781 }
782 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
783 auto& searchSettingsJson =
784 serverTypeJson["LDAPService"]["SearchSettings"];
785 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
786 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
787 if (baseDNList.size() > 1)
788 {
789 messages::propertyValueModified(
790 asyncResp->res, "BaseDistinguishedNames", baseDNList.front());
791 }
792 BMCWEB_LOG_DEBUG << "Updated the base DN";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700793 },
794 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
795 ldapConfigInterface, "LDAPBaseDN",
Ed Tanous168e20c2021-12-13 14:39:53 -0800796 dbus::utility::DbusVariantType(baseDNList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700797}
798/**
799 * @brief updates the LDAP user name attribute and updates the
800 json response with the new value.
801 * @param userNameAttribute attribute to be updated.
802 * @param asyncResp pointer to the JSON response
803 * @param ldapServerElementName Type of LDAP
804 server(openLDAP/ActiveDirectory)
805 */
806
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700807inline void
808 handleUserNameAttrPatch(const std::string& userNameAttribute,
809 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
810 const std::string& ldapServerElementName,
811 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700812{
813 crow::connections::systemBus->async_method_call(
814 [asyncResp, userNameAttribute,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800815 ldapServerElementName](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700816 if (ec)
817 {
818 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
819 "username attribute";
820 messages::internalError(asyncResp->res);
821 return;
822 }
823 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
824 auto& searchSettingsJson =
825 serverTypeJson["LDAPService"]["SearchSettings"];
826 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
827 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700828 },
829 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
830 ldapConfigInterface, "UserNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800831 dbus::utility::DbusVariantType(userNameAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700832}
833/**
834 * @brief updates the LDAP group attribute and updates the
835 json response with the new value.
836 * @param groupsAttribute attribute to be updated.
837 * @param asyncResp pointer to the JSON response
838 * @param ldapServerElementName Type of LDAP
839 server(openLDAP/ActiveDirectory)
840 */
841
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700842inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700843 const std::string& groupsAttribute,
844 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
845 const std::string& ldapServerElementName,
846 const std::string& ldapConfigObject)
847{
848 crow::connections::systemBus->async_method_call(
849 [asyncResp, groupsAttribute,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800850 ldapServerElementName](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700851 if (ec)
852 {
853 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
854 "groupname attribute";
855 messages::internalError(asyncResp->res);
856 return;
857 }
858 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
859 auto& searchSettingsJson =
860 serverTypeJson["LDAPService"]["SearchSettings"];
861 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
862 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700863 },
864 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
865 ldapConfigInterface, "GroupNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800866 dbus::utility::DbusVariantType(groupsAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700867}
868/**
869 * @brief updates the LDAP service enable and updates the
870 json response with the new value.
871 * @param input JSON data.
872 * @param asyncResp pointer to the JSON response
873 * @param ldapServerElementName Type of LDAP
874 server(openLDAP/ActiveDirectory)
875 */
876
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700877inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700878 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
879 const std::string& ldapServerElementName,
880 const std::string& ldapConfigObject)
881{
882 crow::connections::systemBus->async_method_call(
883 [asyncResp, serviceEnabled,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800884 ldapServerElementName](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700885 if (ec)
886 {
887 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the service enable";
888 messages::internalError(asyncResp->res);
889 return;
890 }
891 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
892 serviceEnabled;
893 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700894 },
895 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800896 ldapEnableInterface, "Enabled",
897 dbus::utility::DbusVariantType(serviceEnabled));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700898}
899
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700900inline void
901 handleAuthMethodsPatch(nlohmann::json& input,
902 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700903{
904 std::optional<bool> basicAuth;
905 std::optional<bool> cookie;
906 std::optional<bool> sessionToken;
907 std::optional<bool> xToken;
908 std::optional<bool> tls;
909
910 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
911 "Cookie", cookie, "SessionToken", sessionToken,
912 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530913 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700914 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
915 return;
916 }
917
918 // Make a copy of methods configuration
919 persistent_data::AuthConfigMethods authMethodsConfig =
920 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
921
922 if (basicAuth)
923 {
924#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
925 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800926 asyncResp->res,
927 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700928 return;
929#endif
930 authMethodsConfig.basic = *basicAuth;
931 }
932
933 if (cookie)
934 {
935#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800936 messages::actionNotSupported(
937 asyncResp->res,
938 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700939 return;
940#endif
941 authMethodsConfig.cookie = *cookie;
942 }
943
944 if (sessionToken)
945 {
946#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
947 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800948 asyncResp->res,
949 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700950 return;
951#endif
952 authMethodsConfig.sessionToken = *sessionToken;
953 }
954
955 if (xToken)
956 {
957#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800958 messages::actionNotSupported(
959 asyncResp->res,
960 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700961 return;
962#endif
963 authMethodsConfig.xtoken = *xToken;
964 }
965
966 if (tls)
967 {
968#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800969 messages::actionNotSupported(
970 asyncResp->res,
971 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700972 return;
973#endif
974 authMethodsConfig.tls = *tls;
975 }
976
977 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
978 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
979 !authMethodsConfig.tls)
980 {
981 // Do not allow user to disable everything
982 messages::actionNotSupported(asyncResp->res,
983 "of disabling all available methods");
984 return;
985 }
986
987 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
988 authMethodsConfig);
989 // Save configuration immediately
990 persistent_data::getConfig().writeData();
991
992 messages::success(asyncResp->res);
993}
994
995/**
996 * @brief Get the required values from the given JSON, validates the
997 * value and create the LDAP config object.
998 * @param input JSON data
999 * @param asyncResp pointer to the JSON response
1000 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
1001 */
1002
1003inline void handleLDAPPatch(nlohmann::json& input,
1004 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1005 const std::string& serverType)
1006{
1007 std::string dbusObjectPath;
1008 if (serverType == "ActiveDirectory")
1009 {
1010 dbusObjectPath = adConfigObject;
1011 }
1012 else if (serverType == "LDAP")
1013 {
1014 dbusObjectPath = ldapConfigObjectName;
1015 }
1016 else
1017 {
1018 return;
1019 }
1020
1021 std::optional<nlohmann::json> authentication;
1022 std::optional<nlohmann::json> ldapService;
1023 std::optional<std::vector<std::string>> serviceAddressList;
1024 std::optional<bool> serviceEnabled;
1025 std::optional<std::vector<std::string>> baseDNList;
1026 std::optional<std::string> userNameAttribute;
1027 std::optional<std::string> groupsAttribute;
1028 std::optional<std::string> userName;
1029 std::optional<std::string> password;
1030 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1031
1032 if (!json_util::readJson(input, asyncResp->res, "Authentication",
1033 authentication, "LDAPService", ldapService,
1034 "ServiceAddresses", serviceAddressList,
1035 "ServiceEnabled", serviceEnabled,
1036 "RemoteRoleMapping", remoteRoleMapData))
1037 {
1038 return;
1039 }
1040
1041 if (authentication)
1042 {
1043 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1044 password);
1045 }
1046 if (ldapService)
1047 {
1048 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1049 userNameAttribute, groupsAttribute);
1050 }
1051 if (serviceAddressList)
1052 {
Ed Tanous26f69762022-01-25 09:49:11 -08001053 if (serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301054 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001055 messages::propertyValueNotInList(asyncResp->res, "[]",
1056 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001057 return;
1058 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001059 }
1060 if (baseDNList)
1061 {
Ed Tanous26f69762022-01-25 09:49:11 -08001062 if (baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +05301063 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001064 messages::propertyValueNotInList(asyncResp->res, "[]",
1065 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301066 return;
1067 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001068 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301069
Ed Tanous6c51eab2021-06-03 12:30:29 -07001070 // nothing to update, then return
1071 if (!userName && !password && !serviceAddressList && !baseDNList &&
1072 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1073 !remoteRoleMapData)
1074 {
1075 return;
1076 }
1077
1078 // Get the existing resource first then keep modifying
1079 // whenever any property gets updated.
Ed Tanous002d39b2022-05-31 08:59:27 -07001080 getLDAPConfigData(
1081 serverType,
1082 [asyncResp, userName, password, baseDNList, userNameAttribute,
1083 groupsAttribute, serviceAddressList, serviceEnabled, dbusObjectPath,
1084 remoteRoleMapData](bool success, const LDAPConfigData& confData,
1085 const std::string& serverT) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001086 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301087 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001088 messages::internalError(asyncResp->res);
1089 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301090 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001091 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1092 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301093 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001094 // Disable the service first and update the rest of
1095 // the properties.
1096 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301097 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001098
Ratan Gupta8a07d282019-03-16 08:33:47 +05301099 if (serviceAddressList)
1100 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001101 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1102 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301103 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001104 if (userName)
1105 {
1106 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1107 }
1108 if (password)
1109 {
1110 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1111 }
1112
Ratan Gupta8a07d282019-03-16 08:33:47 +05301113 if (baseDNList)
1114 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001115 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1116 }
1117 if (userNameAttribute)
1118 {
1119 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1120 dbusObjectPath);
1121 }
1122 if (groupsAttribute)
1123 {
1124 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1125 dbusObjectPath);
1126 }
1127 if (serviceEnabled)
1128 {
1129 // if user has given the value as true then enable
1130 // the service. if user has given false then no-op
1131 // as service is already stopped.
1132 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301133 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001134 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1135 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301136 }
1137 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001138 else
1139 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001140 // if user has not given the service enabled value
1141 // then revert it to the same state as it was
1142 // before.
1143 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1144 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001145 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001146
Ed Tanous6c51eab2021-06-03 12:30:29 -07001147 if (remoteRoleMapData)
1148 {
1149 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1150 *remoteRoleMapData);
1151 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001152 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001153}
1154
1155inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1156 const std::string& username,
Ed Tanous618c14b2022-06-30 17:44:25 -07001157 const std::optional<std::string>& password,
1158 const std::optional<bool>& enabled,
1159 const std::optional<std::string>& roleId,
1160 const std::optional<bool>& locked)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001161{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301162 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1163 tempObjPath /= username;
1164 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001165
1166 dbus::utility::checkDbusPathExists(
Ed Tanous618c14b2022-06-30 17:44:25 -07001167 dbusObjectPath, [dbusObjectPath, username, password, roleId, enabled,
1168 locked, asyncResp{std::move(asyncResp)}](int rc) {
1169 if (rc <= 0)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001170 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001171 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1172 username);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001173 return;
1174 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001175
Ed Tanous618c14b2022-06-30 17:44:25 -07001176 if (password)
1177 {
1178 int retval = pamUpdatePassword(username, *password);
1179
1180 if (retval == PAM_USER_UNKNOWN)
1181 {
1182 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1183 username);
1184 }
1185 else if (retval == PAM_AUTHTOK_ERR)
1186 {
1187 // If password is invalid
1188 messages::propertyValueFormatError(asyncResp->res,
1189 *password, "Password");
1190 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1191 }
1192 else if (retval != PAM_SUCCESS)
1193 {
1194 messages::internalError(asyncResp->res);
1195 return;
1196 }
1197 else
1198 {
1199 messages::success(asyncResp->res);
1200 }
1201 }
1202
1203 if (enabled)
1204 {
1205 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001206 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001207 if (ec)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001208 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001209 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001210 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001211 return;
1212 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001213 messages::success(asyncResp->res);
1214 return;
Ed Tanous618c14b2022-06-30 17:44:25 -07001215 },
1216 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1217 "org.freedesktop.DBus.Properties", "Set",
1218 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1219 dbus::utility::DbusVariantType{*enabled});
Ed Tanous002d39b2022-05-31 08:59:27 -07001220 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001221
Ed Tanous618c14b2022-06-30 17:44:25 -07001222 if (roleId)
1223 {
1224 std::string priv = getPrivilegeFromRoleId(*roleId);
1225 if (priv.empty())
1226 {
1227 messages::propertyValueNotInList(asyncResp->res, *roleId,
1228 "RoleId");
1229 return;
1230 }
1231
1232 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001233 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001234 if (ec)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001235 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001236 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1237 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001238 return;
1239 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001240 messages::success(asyncResp->res);
Ed Tanous618c14b2022-06-30 17:44:25 -07001241 },
1242 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1243 "org.freedesktop.DBus.Properties", "Set",
1244 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1245 dbus::utility::DbusVariantType{priv});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001246 }
1247
Ed Tanous618c14b2022-06-30 17:44:25 -07001248 if (locked)
1249 {
1250 // admin can unlock the account which is locked by
1251 // successive authentication failures but admin should
1252 // not be allowed to lock an account.
1253 if (*locked)
1254 {
1255 messages::propertyValueNotInList(asyncResp->res, "true",
1256 "Locked");
1257 return;
1258 }
1259
1260 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001261 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001262 if (ec)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001263 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001264 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1265 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001266 return;
1267 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001268 messages::success(asyncResp->res);
1269 return;
Ed Tanous618c14b2022-06-30 17:44:25 -07001270 },
1271 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1272 "org.freedesktop.DBus.Properties", "Set",
1273 "xyz.openbmc_project.User.Attributes",
1274 "UserLockedForFailedAttempt",
1275 dbus::utility::DbusVariantType{*locked});
1276 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001277 });
1278}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001279
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001280inline void handleAccountServiceHead(
1281 App& app, const crow::Request& req,
1282 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001283{
1284 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1285 {
1286 return;
1287 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001288 asyncResp->res.addHeader(
1289 boost::beast::http::field::link,
1290 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1291}
1292
1293inline void
1294 handleAccountServiceGet(App& app, const crow::Request& req,
1295 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1296{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001297 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1298 {
1299 return;
1300 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001301
1302 if (req.session == nullptr)
1303 {
1304 messages::internalError(asyncResp->res);
1305 return;
1306 }
1307
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001308 asyncResp->res.addHeader(
1309 boost::beast::http::field::link,
1310 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1311
Ed Tanous1ef4c342022-05-12 16:12:36 -07001312 const persistent_data::AuthConfigMethods& authMethodsConfig =
1313 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1314
1315 nlohmann::json& json = asyncResp->res.jsonValue;
1316 json["@odata.id"] = "/redfish/v1/AccountService";
1317 json["@odata.type"] = "#AccountService."
1318 "v1_10_0.AccountService";
1319 json["Id"] = "AccountService";
1320 json["Name"] = "Account Service";
1321 json["Description"] = "Account Service";
1322 json["ServiceEnabled"] = true;
1323 json["MaxPasswordLength"] = 20;
1324 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1325 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
1326 json["Oem"]["OpenBMC"]["@odata.type"] =
Ed Tanous5b5574a2022-09-26 19:53:36 -07001327 "#OpenBMCAccountService.v1_0_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001328 json["Oem"]["OpenBMC"]["@odata.id"] =
1329 "/redfish/v1/AccountService#/Oem/OpenBMC";
1330 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1331 authMethodsConfig.basic;
1332 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1333 authMethodsConfig.sessionToken;
1334 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1335 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1336 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1337
1338 // /redfish/v1/AccountService/LDAP/Certificates is something only
1339 // ConfigureManager can access then only display when the user has
1340 // permissions ConfigureManager
1341 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001342 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001343
1344 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1345 effectiveUserPrivileges))
1346 {
1347 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1348 "/redfish/v1/AccountService/LDAP/Certificates";
1349 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001350 sdbusplus::asio::getAllProperties(
1351 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1352 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.AccountPolicy",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001353 [asyncResp](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001354 const dbus::utility::DBusPropertiesMap& propertiesList) {
1355 if (ec)
1356 {
1357 messages::internalError(asyncResp->res);
1358 return;
1359 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001360
Ed Tanous1ef4c342022-05-12 16:12:36 -07001361 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1362 << "properties for AccountService";
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001363
1364 const uint8_t* minPasswordLength = nullptr;
1365 const uint32_t* accountUnlockTimeout = nullptr;
1366 const uint16_t* maxLoginAttemptBeforeLockout = nullptr;
1367
1368 const bool success = sdbusplus::unpackPropertiesNoThrow(
1369 dbus_utils::UnpackErrorPrinter(), propertiesList,
1370 "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout",
1371 accountUnlockTimeout, "MaxLoginAttemptBeforeLockout",
1372 maxLoginAttemptBeforeLockout);
1373
1374 if (!success)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001375 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001376 messages::internalError(asyncResp->res);
1377 return;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001378 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001379
1380 if (minPasswordLength != nullptr)
1381 {
1382 asyncResp->res.jsonValue["MinPasswordLength"] = *minPasswordLength;
1383 }
1384
1385 if (accountUnlockTimeout != nullptr)
1386 {
1387 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1388 *accountUnlockTimeout;
1389 }
1390
1391 if (maxLoginAttemptBeforeLockout != nullptr)
1392 {
1393 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1394 *maxLoginAttemptBeforeLockout;
1395 }
1396 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001397
Ed Tanous02cad962022-06-30 16:50:15 -07001398 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001399 const std::string& ldapType) {
1400 if (!success)
1401 {
1402 return;
1403 }
1404 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1405 };
1406
1407 getLDAPConfigData("LDAP", callback);
1408 getLDAPConfigData("ActiveDirectory", callback);
1409}
1410
1411inline void handleAccountServicePatch(
1412 App& app, const crow::Request& req,
1413 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1414{
1415 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1416 {
1417 return;
1418 }
1419 std::optional<uint32_t> unlockTimeout;
1420 std::optional<uint16_t> lockoutThreshold;
1421 std::optional<uint8_t> minPasswordLength;
1422 std::optional<uint16_t> maxPasswordLength;
1423 std::optional<nlohmann::json> ldapObject;
1424 std::optional<nlohmann::json> activeDirectoryObject;
1425 std::optional<nlohmann::json> oemObject;
1426
1427 if (!json_util::readJsonPatch(
1428 req, asyncResp->res, "AccountLockoutDuration", unlockTimeout,
1429 "AccountLockoutThreshold", lockoutThreshold, "MaxPasswordLength",
1430 maxPasswordLength, "MinPasswordLength", minPasswordLength, "LDAP",
1431 ldapObject, "ActiveDirectory", activeDirectoryObject, "Oem",
1432 oemObject))
1433 {
1434 return;
1435 }
1436
1437 if (minPasswordLength)
1438 {
1439 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001440 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001441 if (ec)
1442 {
1443 messages::internalError(asyncResp->res);
1444 return;
1445 }
1446 messages::success(asyncResp->res);
1447 },
1448 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1449 "org.freedesktop.DBus.Properties", "Set",
1450 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
1451 dbus::utility::DbusVariantType(*minPasswordLength));
1452 }
1453
1454 if (maxPasswordLength)
1455 {
1456 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1457 }
1458
1459 if (ldapObject)
1460 {
1461 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1462 }
1463
1464 if (std::optional<nlohmann::json> oemOpenBMCObject;
1465 oemObject && json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1466 oemOpenBMCObject))
1467 {
1468 if (std::optional<nlohmann::json> authMethodsObject;
1469 oemOpenBMCObject &&
1470 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1471 "AuthMethods", authMethodsObject))
1472 {
1473 if (authMethodsObject)
1474 {
1475 handleAuthMethodsPatch(*authMethodsObject, asyncResp);
1476 }
1477 }
1478 }
1479
1480 if (activeDirectoryObject)
1481 {
1482 handleLDAPPatch(*activeDirectoryObject, asyncResp, "ActiveDirectory");
1483 }
1484
1485 if (unlockTimeout)
1486 {
1487 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001488 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001489 if (ec)
1490 {
1491 messages::internalError(asyncResp->res);
1492 return;
1493 }
1494 messages::success(asyncResp->res);
1495 },
1496 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1497 "org.freedesktop.DBus.Properties", "Set",
1498 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
1499 dbus::utility::DbusVariantType(*unlockTimeout));
1500 }
1501 if (lockoutThreshold)
1502 {
1503 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001504 [asyncResp](const boost::system::error_code& ec) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001505 if (ec)
1506 {
1507 messages::internalError(asyncResp->res);
1508 return;
1509 }
1510 messages::success(asyncResp->res);
1511 },
1512 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1513 "org.freedesktop.DBus.Properties", "Set",
1514 "xyz.openbmc_project.User.AccountPolicy",
1515 "MaxLoginAttemptBeforeLockout",
1516 dbus::utility::DbusVariantType(*lockoutThreshold));
1517 }
1518}
1519
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001520inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001521 App& app, const crow::Request& req,
1522 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1523{
1524 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1525 {
1526 return;
1527 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001528 asyncResp->res.addHeader(
1529 boost::beast::http::field::link,
1530 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1531}
1532
1533inline void handleAccountCollectionGet(
1534 App& app, const crow::Request& req,
1535 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1536{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001537 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1538 {
1539 return;
1540 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001541
1542 if (req.session == nullptr)
1543 {
1544 messages::internalError(asyncResp->res);
1545 return;
1546 }
1547
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001548 asyncResp->res.addHeader(
1549 boost::beast::http::field::link,
1550 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001551
1552 asyncResp->res.jsonValue["@odata.id"] =
1553 "/redfish/v1/AccountService/Accounts";
1554 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1555 "ManagerAccountCollection";
1556 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1557 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1558
1559 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001560 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001561
1562 std::string thisUser;
1563 if (req.session)
1564 {
1565 thisUser = req.session->username;
1566 }
1567 crow::connections::systemBus->async_method_call(
1568 [asyncResp, thisUser, effectiveUserPrivileges](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001569 const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001570 const dbus::utility::ManagedObjectType& users) {
1571 if (ec)
1572 {
1573 messages::internalError(asyncResp->res);
1574 return;
1575 }
1576
1577 bool userCanSeeAllAccounts =
1578 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1579
1580 bool userCanSeeSelf =
1581 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1582
1583 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1584 memberArray = nlohmann::json::array();
1585
1586 for (const auto& userpath : users)
1587 {
1588 std::string user = userpath.first.filename();
1589 if (user.empty())
1590 {
1591 messages::internalError(asyncResp->res);
1592 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1593
1594 return;
1595 }
1596
1597 // As clarified by Redfish here:
1598 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1599 // Users without ConfigureUsers, only see their own
1600 // account. Users with ConfigureUsers, see all
1601 // accounts.
1602 if (userCanSeeAllAccounts || (thisUser == user && userCanSeeSelf))
1603 {
1604 nlohmann::json::object_t member;
Patrick Williams89492a12023-05-10 07:51:34 -05001605 member["@odata.id"] = "/redfish/v1/AccountService/Accounts/" +
1606 user;
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001607 memberArray.emplace_back(std::move(member));
Ed Tanous1ef4c342022-05-12 16:12:36 -07001608 }
1609 }
1610 asyncResp->res.jsonValue["Members@odata.count"] = memberArray.size();
1611 },
1612 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1613 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1614}
1615
Ninad Palsule97e90da2023-05-17 14:04:52 -05001616inline void processAfterCreateUser(
1617 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1618 const std::string& username, const std::string& password,
1619 const boost::system::error_code& ec, sdbusplus::message_t& m)
1620{
1621 if (ec)
1622 {
1623 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1624 return;
1625 }
1626
1627 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1628 {
1629 // At this point we have a user that's been
1630 // created, but the password set
1631 // failed.Something is wrong, so delete the user
1632 // that we've already created
1633 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1634 tempObjPath /= username;
1635 const std::string userPath(tempObjPath);
1636
1637 crow::connections::systemBus->async_method_call(
1638 [asyncResp, password](const boost::system::error_code& ec3) {
1639 if (ec3)
1640 {
1641 messages::internalError(asyncResp->res);
1642 return;
1643 }
1644
1645 // If password is invalid
1646 messages::propertyValueFormatError(asyncResp->res, password,
1647 "Password");
1648 },
1649 "xyz.openbmc_project.User.Manager", userPath,
1650 "xyz.openbmc_project.Object.Delete", "Delete");
1651
1652 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1653 return;
1654 }
1655
1656 messages::created(asyncResp->res);
1657 asyncResp->res.addHeader("Location",
1658 "/redfish/v1/AccountService/Accounts/" + username);
1659}
1660
1661inline void processAfterGetAllGroups(
1662 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1663 const std::string& username, const std::string& password,
1664 const std::optional<std::string>& roleId, std::optional<bool> enabled,
1665 const std::vector<std::string>& allGroupsList)
1666
1667{
Ninad Palsule3e72c202023-03-27 17:19:55 -05001668 std::vector<std::string> userGroups;
1669 for (const auto& grp : allGroupsList)
1670 {
1671 // Console access is provided to the user who is a member of
1672 // hostconsole group and has a administrator role. So, set
1673 // hostconsole group only for the administrator.
1674 if ((grp != "hostconsole") || (roleId == "priv-admin"))
1675 {
1676 userGroups.emplace_back(grp);
1677 }
1678 }
1679
Ninad Palsule97e90da2023-05-17 14:04:52 -05001680 crow::connections::systemBus->async_method_call(
1681 [asyncResp, username, password](const boost::system::error_code& ec2,
1682 sdbusplus::message_t& m) {
1683 processAfterCreateUser(asyncResp, username, password, ec2, m);
1684 },
1685 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ninad Palsule3e72c202023-03-27 17:19:55 -05001686 "xyz.openbmc_project.User.Manager", "CreateUser", username, userGroups,
1687 *roleId, *enabled);
Ninad Palsule97e90da2023-05-17 14:04:52 -05001688}
1689
Ed Tanous1ef4c342022-05-12 16:12:36 -07001690inline void handleAccountCollectionPost(
1691 App& app, const crow::Request& req,
1692 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1693{
1694 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1695 {
1696 return;
1697 }
1698 std::string username;
1699 std::string password;
1700 std::optional<std::string> roleId("User");
1701 std::optional<bool> enabled = true;
1702 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", username,
1703 "Password", password, "RoleId", roleId,
1704 "Enabled", enabled))
1705 {
1706 return;
1707 }
1708
1709 std::string priv = getPrivilegeFromRoleId(*roleId);
1710 if (priv.empty())
1711 {
1712 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
1713 return;
1714 }
Asmitha Karunanithi239adf82022-03-25 02:59:03 -05001715 roleId = priv;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001716
1717 // Reading AllGroups property
1718 sdbusplus::asio::getProperty<std::vector<std::string>>(
1719 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1720 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager",
1721 "AllGroups",
1722 [asyncResp, username, password{std::move(password)}, roleId,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001723 enabled](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001724 const std::vector<std::string>& allGroupsList) {
1725 if (ec)
1726 {
1727 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1728 messages::internalError(asyncResp->res);
1729 return;
1730 }
1731
1732 if (allGroupsList.empty())
1733 {
1734 messages::internalError(asyncResp->res);
1735 return;
1736 }
1737
Ninad Palsule97e90da2023-05-17 14:04:52 -05001738 processAfterGetAllGroups(asyncResp, username, password, roleId, enabled,
1739 allGroupsList);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001740 });
1741}
1742
1743inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001744 handleAccountHead(App& app, const crow::Request& req,
1745 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1746 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001747{
1748 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1749 {
1750 return;
1751 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001752 asyncResp->res.addHeader(
1753 boost::beast::http::field::link,
1754 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1755}
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001756
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001757inline void
1758 handleAccountGet(App& app, const crow::Request& req,
1759 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1760 const std::string& accountName)
1761{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001762 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1763 {
1764 return;
1765 }
1766 asyncResp->res.addHeader(
1767 boost::beast::http::field::link,
1768 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1769
Ed Tanous1ef4c342022-05-12 16:12:36 -07001770#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1771 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001772 messages::resourceNotFound(asyncResp->res, "ManagerAccount", accountName);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001773 return;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001774#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001775
Ed Tanous1ef4c342022-05-12 16:12:36 -07001776 if (req.session == nullptr)
1777 {
1778 messages::internalError(asyncResp->res);
1779 return;
1780 }
1781 if (req.session->username != accountName)
1782 {
1783 // At this point we've determined that the user is trying to
1784 // modify a user that isn't them. We need to verify that they
1785 // have permissions to modify other users, so re-run the auth
1786 // check with the same permissions, minus ConfigureSelf.
1787 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001788 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001789 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
1790 "ConfigureManager"};
1791 if (!effectiveUserPrivileges.isSupersetOf(
1792 requiredPermissionsToChangeNonSelf))
1793 {
1794 BMCWEB_LOG_DEBUG << "GET Account denied access";
1795 messages::insufficientPrivilege(asyncResp->res);
1796 return;
1797 }
1798 }
1799
1800 crow::connections::systemBus->async_method_call(
1801 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001802 accountName](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001803 const dbus::utility::ManagedObjectType& users) {
1804 if (ec)
1805 {
1806 messages::internalError(asyncResp->res);
1807 return;
1808 }
1809 const auto userIt = std::find_if(
1810 users.begin(), users.end(),
1811 [accountName](
1812 const std::pair<sdbusplus::message::object_path,
1813 dbus::utility::DBusInteracesMap>& user) {
1814 return accountName == user.first.filename();
1815 });
1816
1817 if (userIt == users.end())
1818 {
1819 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1820 accountName);
1821 return;
1822 }
1823
1824 asyncResp->res.jsonValue["@odata.type"] =
1825 "#ManagerAccount.v1_4_0.ManagerAccount";
1826 asyncResp->res.jsonValue["Name"] = "User Account";
1827 asyncResp->res.jsonValue["Description"] = "User Account";
1828 asyncResp->res.jsonValue["Password"] = nullptr;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001829
1830 for (const auto& interface : userIt->second)
1831 {
1832 if (interface.first == "xyz.openbmc_project.User.Attributes")
1833 {
1834 for (const auto& property : interface.second)
1835 {
1836 if (property.first == "UserEnabled")
1837 {
1838 const bool* userEnabled =
1839 std::get_if<bool>(&property.second);
1840 if (userEnabled == nullptr)
1841 {
1842 BMCWEB_LOG_ERROR << "UserEnabled wasn't a bool";
1843 messages::internalError(asyncResp->res);
1844 return;
1845 }
1846 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
1847 }
1848 else if (property.first == "UserLockedForFailedAttempt")
1849 {
1850 const bool* userLocked =
1851 std::get_if<bool>(&property.second);
1852 if (userLocked == nullptr)
1853 {
1854 BMCWEB_LOG_ERROR << "UserLockedForF"
1855 "ailedAttempt "
1856 "wasn't a bool";
1857 messages::internalError(asyncResp->res);
1858 return;
1859 }
1860 asyncResp->res.jsonValue["Locked"] = *userLocked;
1861 asyncResp->res
1862 .jsonValue["Locked@Redfish.AllowableValues"] = {
1863 "false"}; // can only unlock accounts
1864 }
1865 else if (property.first == "UserPrivilege")
1866 {
1867 const std::string* userPrivPtr =
1868 std::get_if<std::string>(&property.second);
1869 if (userPrivPtr == nullptr)
1870 {
1871 BMCWEB_LOG_ERROR << "UserPrivilege wasn't a "
1872 "string";
1873 messages::internalError(asyncResp->res);
1874 return;
1875 }
1876 std::string role = getRoleIdFromPrivilege(*userPrivPtr);
1877 if (role.empty())
1878 {
1879 BMCWEB_LOG_ERROR << "Invalid user role";
1880 messages::internalError(asyncResp->res);
1881 return;
1882 }
1883 asyncResp->res.jsonValue["RoleId"] = role;
1884
1885 nlohmann::json& roleEntry =
1886 asyncResp->res.jsonValue["Links"]["Role"];
1887 roleEntry["@odata.id"] =
1888 "/redfish/v1/AccountService/Roles/" + role;
1889 }
1890 else if (property.first == "UserPasswordExpired")
1891 {
1892 const bool* userPasswordExpired =
1893 std::get_if<bool>(&property.second);
1894 if (userPasswordExpired == nullptr)
1895 {
1896 BMCWEB_LOG_ERROR
1897 << "UserPasswordExpired wasn't a bool";
1898 messages::internalError(asyncResp->res);
1899 return;
1900 }
1901 asyncResp->res.jsonValue["PasswordChangeRequired"] =
1902 *userPasswordExpired;
1903 }
Abhishek Patelc7229812022-02-01 10:07:15 -06001904 else if (property.first == "UserGroups")
1905 {
1906 const std::vector<std::string>* userGroups =
1907 std::get_if<std::vector<std::string>>(
1908 &property.second);
1909 if (userGroups == nullptr)
1910 {
1911 BMCWEB_LOG_ERROR
1912 << "userGroups wasn't a string vector";
1913 messages::internalError(asyncResp->res);
1914 return;
1915 }
1916 if (!translateUserGroup(*userGroups, asyncResp->res))
1917 {
1918 BMCWEB_LOG_ERROR << "userGroups mapping failed";
1919 messages::internalError(asyncResp->res);
1920 return;
1921 }
1922 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001923 }
1924 }
1925 }
1926
1927 asyncResp->res.jsonValue["@odata.id"] =
1928 "/redfish/v1/AccountService/Accounts/" + accountName;
1929 asyncResp->res.jsonValue["Id"] = accountName;
1930 asyncResp->res.jsonValue["UserName"] = accountName;
1931 },
1932 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1933 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1934}
1935
1936inline void
Gunnar Mills20fc3072023-01-27 15:13:36 -06001937 handleAccountDelete(App& app, const crow::Request& req,
1938 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1939 const std::string& username)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001940{
1941 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1942 {
1943 return;
1944 }
1945
1946#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1947 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001948 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001949 return;
1950
1951#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1952 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1953 tempObjPath /= username;
1954 const std::string userPath(tempObjPath);
1955
1956 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001957 [asyncResp, username](const boost::system::error_code& ec) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001958 if (ec)
1959 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001960 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
Ed Tanous1ef4c342022-05-12 16:12:36 -07001961 username);
1962 return;
1963 }
1964
1965 messages::accountRemoved(asyncResp->res);
1966 },
1967 "xyz.openbmc_project.User.Manager", userPath,
1968 "xyz.openbmc_project.Object.Delete", "Delete");
1969}
1970
1971inline void
1972 handleAccountPatch(App& app, const crow::Request& req,
1973 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1974 const std::string& username)
1975{
1976 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1977 {
1978 return;
1979 }
1980#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1981 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001982 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001983 return;
1984
1985#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1986 std::optional<std::string> newUserName;
1987 std::optional<std::string> password;
1988 std::optional<bool> enabled;
1989 std::optional<std::string> roleId;
1990 std::optional<bool> locked;
1991
1992 if (req.session == nullptr)
1993 {
1994 messages::internalError(asyncResp->res);
1995 return;
1996 }
1997
1998 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001999 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002000 Privileges configureUsers = {"ConfigureUsers"};
2001 bool userHasConfigureUsers =
2002 effectiveUserPrivileges.isSupersetOf(configureUsers);
2003 if (userHasConfigureUsers)
2004 {
2005 // Users with ConfigureUsers can modify for all users
2006 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
2007 newUserName, "Password", password,
2008 "RoleId", roleId, "Enabled", enabled,
2009 "Locked", locked))
2010 {
2011 return;
2012 }
2013 }
2014 else
2015 {
2016 // ConfigureSelf accounts can only modify their own account
2017 if (username != req.session->username)
2018 {
2019 messages::insufficientPrivilege(asyncResp->res);
2020 return;
2021 }
2022
2023 // ConfigureSelf accounts can only modify their password
2024 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
2025 password))
2026 {
2027 return;
2028 }
2029 }
2030
2031 // if user name is not provided in the patch method or if it
2032 // matches the user name in the URI, then we are treating it as
2033 // updating user properties other then username. If username
2034 // provided doesn't match the URI, then we are treating this as
2035 // user rename request.
2036 if (!newUserName || (newUserName.value() == username))
2037 {
2038 updateUserProperties(asyncResp, username, password, enabled, roleId,
2039 locked);
2040 return;
2041 }
2042 crow::connections::systemBus->async_method_call(
2043 [asyncResp, username, password(std::move(password)),
2044 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002045 locked](const boost::system::error_code& ec, sdbusplus::message_t& m) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07002046 if (ec)
2047 {
2048 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
2049 username);
2050 return;
2051 }
2052
2053 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
2054 locked);
2055 },
2056 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
2057 "xyz.openbmc_project.User.Manager", "RenameUser", username,
2058 *newUserName);
2059}
2060
Ed Tanous6c51eab2021-06-03 12:30:29 -07002061inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07002062{
Ed Tanous6c51eab2021-06-03 12:30:29 -07002063 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002064 .privileges(redfish::privileges::headAccountService)
2065 .methods(boost::beast::http::verb::head)(
2066 std::bind_front(handleAccountServiceHead, std::ref(app)));
2067
2068 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07002069 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002070 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002071 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002072
Ed Tanousf5ffd802021-07-19 10:55:33 -07002073 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06002074 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07002075 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002076 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07002077
Ed Tanous6c51eab2021-06-03 12:30:29 -07002078 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002079 .privileges(redfish::privileges::headManagerAccountCollection)
2080 .methods(boost::beast::http::verb::head)(
2081 std::bind_front(handleAccountCollectionHead, std::ref(app)));
2082
2083 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002084 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002085 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002086 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07002087
Ed Tanous6c51eab2021-06-03 12:30:29 -07002088 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002089 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002090 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002091 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07002092
2093 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002094 .privileges(redfish::privileges::headManagerAccount)
2095 .methods(boost::beast::http::verb::head)(
2096 std::bind_front(handleAccountHead, std::ref(app)));
2097
2098 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07002099 .privileges(redfish::privileges::getManagerAccount)
2100 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002101 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002102
2103 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002104 // TODO this privilege should be using the generated endpoints, but
2105 // because of the special handling of ConfigureSelf, it's not able to
2106 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07002107 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
2108 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002109 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002110
2111 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002112 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002113 .methods(boost::beast::http::verb::delete_)(
Gunnar Mills20fc3072023-01-27 15:13:36 -06002114 std::bind_front(handleAccountDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002115}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002116
Ed Tanous1abe55e2018-09-05 08:30:59 -07002117} // namespace redfish