blob: b6316ef9153a15219cffd4b97705d7414be3ab6a [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 Tanous45ca1b82022-03-25 13:07:27 -070023#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070024#include <registries/privilege_registry.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070025#include <sdbusplus/asio/property.hpp>
Ed Tanousa8408792018-09-05 16:08:38 -070026#include <utils/json_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace redfish
29{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010030
Ed Tanous23a21a12020-07-25 04:45:05 +000031constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053032 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070033constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053034 "/xyz/openbmc_project/user/ldap/active_directory";
35
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053036constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053037constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
38constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
39constexpr const char* ldapConfigInterface =
40 "xyz.openbmc_project.User.Ldap.Config";
41constexpr const char* ldapCreateInterface =
42 "xyz.openbmc_project.User.Ldap.Create";
43constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053044constexpr const char* ldapPrivMapperInterface =
45 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053046constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
47constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
48constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
49constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
50constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
51
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060052struct LDAPRoleMapData
53{
54 std::string groupName;
55 std::string privilege;
56};
57
Ratan Gupta6973a582018-12-13 18:25:44 +053058struct LDAPConfigData
59{
60 std::string uri{};
61 std::string bindDN{};
62 std::string baseDN{};
63 std::string searchScope{};
64 std::string serverType{};
65 bool serviceEnabled = false;
66 std::string userNameAttribute{};
67 std::string groupAttribute{};
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060068 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053069};
70
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060071inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053072{
73 if (role == "priv-admin")
74 {
75 return "Administrator";
76 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070077 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053078 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053079 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053080 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070081 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053082 {
83 return "Operator";
84 }
Ed Tanous26f69762022-01-25 09:49:11 -080085 if (role.empty() || (role == "priv-noaccess"))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +000086 {
87 return "NoAccess";
88 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +053089 return "";
90}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060091inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053092{
93 if (role == "Administrator")
94 {
95 return "priv-admin";
96 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070097 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053098 {
99 return "priv-user";
100 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700101 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530102 {
103 return "priv-operator";
104 }
Ed Tanous26f69762022-01-25 09:49:11 -0800105 if ((role == "NoAccess") || (role.empty()))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +0000106 {
107 return "priv-noaccess";
108 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530109 return "";
110}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700111
zhanghch058d1b46d2021-04-01 11:18:24 +0800112inline void userErrorMessageHandler(
113 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
114 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000115{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000116 if (e == nullptr)
117 {
118 messages::internalError(asyncResp->res);
119 return;
120 }
121
Manojkiran Eda055806b2020-11-03 09:36:28 +0530122 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000123 if (strcmp(errorMessage,
124 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
125 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800126 messages::resourceAlreadyExists(asyncResp->res, "ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000127 "UserName", newUser);
128 }
129 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
130 "UserNameDoesNotExist") == 0)
131 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800132 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000133 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700134 else if ((strcmp(errorMessage,
135 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
136 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800137 (strcmp(
138 errorMessage,
139 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
140 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000141 {
142 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
143 }
144 else if (strcmp(errorMessage,
145 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
146 {
147 messages::createLimitReachedForResource(asyncResp->res);
148 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000149 else
150 {
151 messages::internalError(asyncResp->res);
152 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000153}
154
Ed Tanous81ce6092020-12-17 16:54:55 +0000155inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000156 const LDAPConfigData& confData,
157 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530158{
Ratan Guptaab828d72019-04-22 14:18:33 +0530159 std::string service =
160 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600161
Ed Tanous14766872022-03-15 10:44:42 -0700162 nlohmann::json& ldap = jsonResponse[ldapType];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600163
Ed Tanous14766872022-03-15 10:44:42 -0700164 ldap["ServiceEnabled"] = confData.serviceEnabled;
165 ldap["ServiceAddresses"] = nlohmann::json::array({confData.uri});
166 ldap["Authentication"]["AuthenticationType"] = "UsernameAndPassword";
167 ldap["Authentication"]["Username"] = confData.bindDN;
168 ldap["Authentication"]["Password"] = nullptr;
169
170 ldap["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"] =
171 nlohmann::json::array({confData.baseDN});
172 ldap["LDAPService"]["SearchSettings"]["UsernameAttribute"] =
173 confData.userNameAttribute;
174 ldap["LDAPService"]["SearchSettings"]["GroupsAttribute"] =
175 confData.groupAttribute;
176
177 nlohmann::json& roleMapArray = ldap["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600178 roleMapArray = nlohmann::json::array();
Ed Tanous9eb808c2022-01-25 10:19:23 -0800179 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600180 {
181 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
182 << obj.second.groupName << "\n";
183 roleMapArray.push_back(
184 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
185 nlohmann::json::array(
186 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
187 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530188}
189
190/**
Ratan Gupta06785242019-07-26 22:30:16 +0530191 * @brief validates given JSON input and then calls appropriate method to
192 * create, to delete or to set Rolemapping object based on the given input.
193 *
194 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000195inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800196 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530197 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700198 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530199{
200 for (size_t index = 0; index < input.size(); index++)
201 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700202 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530203
204 if (thisJson.is_null())
205 {
206 // delete the existing object
207 if (index < roleMapObjData.size())
208 {
209 crow::connections::systemBus->async_method_call(
210 [asyncResp, roleMapObjData, serverType,
211 index](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700212 if (ec)
213 {
214 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
215 messages::internalError(asyncResp->res);
216 return;
217 }
218 asyncResp->res
219 .jsonValue[serverType]["RemoteRoleMapping"][index] =
220 nullptr;
Ratan Gupta06785242019-07-26 22:30:16 +0530221 },
222 ldapDbusService, roleMapObjData[index].first,
223 "xyz.openbmc_project.Object.Delete", "Delete");
224 }
225 else
226 {
227 BMCWEB_LOG_ERROR << "Can't delete the object";
228 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800229 asyncResp->res,
230 thisJson.dump(2, ' ', true,
231 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530232 "RemoteRoleMapping/" + std::to_string(index));
233 return;
234 }
235 }
236 else if (thisJson.empty())
237 {
238 // Don't do anything for the empty objects,parse next json
239 // eg {"RemoteRoleMapping",[{}]}
240 }
241 else
242 {
243 // update/create the object
244 std::optional<std::string> remoteGroup;
245 std::optional<std::string> localRole;
246
Ed Tanousf23b7292020-10-15 09:41:17 -0700247 // This is a copy, but it's required in this case because of how
248 // readJson is structured
249 nlohmann::json thisJsonCopy = thisJson;
250 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
251 "RemoteGroup", remoteGroup, "LocalRole",
252 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530253 {
254 continue;
255 }
256
257 // Update existing RoleMapping Object
258 if (index < roleMapObjData.size())
259 {
260 BMCWEB_LOG_DEBUG << "Update Role Map Object";
261 // If "RemoteGroup" info is provided
262 if (remoteGroup)
263 {
264 crow::connections::systemBus->async_method_call(
265 [asyncResp, roleMapObjData, serverType, index,
266 remoteGroup](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 }
273 asyncResp->res
274 .jsonValue[serverType]["RemoteRoleMapping"][index]
275 ["RemoteGroup"] = *remoteGroup;
Ratan Gupta06785242019-07-26 22:30:16 +0530276 },
277 ldapDbusService, roleMapObjData[index].first,
278 propertyInterface, "Set",
279 "xyz.openbmc_project.User.PrivilegeMapperEntry",
280 "GroupName",
Ed Tanous168e20c2021-12-13 14:39:53 -0800281 dbus::utility::DbusVariantType(
282 std::move(*remoteGroup)));
Ratan Gupta06785242019-07-26 22:30:16 +0530283 }
284
285 // If "LocalRole" info is provided
286 if (localRole)
287 {
288 crow::connections::systemBus->async_method_call(
289 [asyncResp, roleMapObjData, serverType, index,
290 localRole](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700291 if (ec)
292 {
293 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
294 messages::internalError(asyncResp->res);
295 return;
296 }
297 asyncResp->res
298 .jsonValue[serverType]["RemoteRoleMapping"][index]
299 ["LocalRole"] = *localRole;
Ratan Gupta06785242019-07-26 22:30:16 +0530300 },
301 ldapDbusService, roleMapObjData[index].first,
302 propertyInterface, "Set",
303 "xyz.openbmc_project.User.PrivilegeMapperEntry",
304 "Privilege",
Ed Tanous168e20c2021-12-13 14:39:53 -0800305 dbus::utility::DbusVariantType(
Ratan Gupta06785242019-07-26 22:30:16 +0530306 getPrivilegeFromRoleId(std::move(*localRole))));
307 }
308 }
309 // Create a new RoleMapping Object.
310 else
311 {
312 BMCWEB_LOG_DEBUG
313 << "setRoleMappingProperties: Creating new Object";
314 std::string pathString =
315 "RemoteRoleMapping/" + std::to_string(index);
316
317 if (!localRole)
318 {
319 messages::propertyMissing(asyncResp->res,
320 pathString + "/LocalRole");
321 continue;
322 }
323 if (!remoteGroup)
324 {
325 messages::propertyMissing(asyncResp->res,
326 pathString + "/RemoteGroup");
327 continue;
328 }
329
330 std::string dbusObjectPath;
331 if (serverType == "ActiveDirectory")
332 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700333 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530334 }
335 else if (serverType == "LDAP")
336 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000337 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530338 }
339
340 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
341 << ",LocalRole=" << *localRole;
342
343 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700344 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530345 remoteGroup](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700346 if (ec)
347 {
348 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
349 messages::internalError(asyncResp->res);
350 return;
351 }
352 nlohmann::json& remoteRoleJson =
353 asyncResp->res
354 .jsonValue[serverType]["RemoteRoleMapping"];
355 nlohmann::json::object_t roleMapEntry;
356 roleMapEntry["LocalRole"] = *localRole;
357 roleMapEntry["RemoteGroup"] = *remoteGroup;
358 remoteRoleJson.push_back(std::move(roleMapEntry));
Ratan Gupta06785242019-07-26 22:30:16 +0530359 },
360 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700361 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530362 getPrivilegeFromRoleId(std::move(*localRole)));
363 }
364 }
365 }
366}
367
368/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530369 * Function that retrieves all properties for LDAP config object
370 * into JSON
371 */
372template <typename CallbackFunc>
373inline void getLDAPConfigData(const std::string& ldapType,
374 CallbackFunc&& callback)
375{
Ratan Guptaab828d72019-04-22 14:18:33 +0530376
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600377 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530378 ldapConfigInterface};
379
380 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600381 [callback, ldapType](const boost::system::error_code ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800382 const dbus::utility::MapperGetObject& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700383 if (ec || resp.empty())
384 {
385 BMCWEB_LOG_ERROR
386 << "DBUS response error during getting of service name: " << ec;
387 LDAPConfigData empty{};
388 callback(false, empty, ldapType);
389 return;
390 }
391 std::string service = resp.begin()->first;
392 crow::connections::systemBus->async_method_call(
393 [callback,
394 ldapType](const boost::system::error_code errorCode,
395 const dbus::utility::ManagedObjectType& ldapObjects) {
396 LDAPConfigData confData{};
397 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600398 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700399 callback(false, confData, ldapType);
400 BMCWEB_LOG_ERROR << "D-Bus responses error: " << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600401 return;
402 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600403
Ed Tanous002d39b2022-05-31 08:59:27 -0700404 std::string ldapDbusType;
405 std::string searchString;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600406
Ed Tanous002d39b2022-05-31 08:59:27 -0700407 if (ldapType == "LDAP")
408 {
409 ldapDbusType =
410 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
411 searchString = "openldap";
412 }
413 else if (ldapType == "ActiveDirectory")
414 {
415 ldapDbusType =
416 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
417 searchString = "active_directory";
418 }
419 else
420 {
421 BMCWEB_LOG_ERROR << "Can't get the DbusType for the given type="
422 << ldapType;
423 callback(false, confData, ldapType);
424 return;
425 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600426
Ed Tanous002d39b2022-05-31 08:59:27 -0700427 std::string ldapEnableInterfaceStr = ldapEnableInterface;
428 std::string ldapConfigInterfaceStr = ldapConfigInterface;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600429
Ed Tanous002d39b2022-05-31 08:59:27 -0700430 for (const auto& object : ldapObjects)
431 {
432 // let's find the object whose ldap type is equal to the
433 // given type
434 if (object.first.str.find(searchString) == std::string::npos)
435 {
436 continue;
437 }
438
439 for (const auto& interface : object.second)
440 {
441 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600442 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700443 // rest of the properties are string.
444 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600445 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700446 if (property.first == "Enabled")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600447 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700448 const bool* value =
449 std::get_if<bool>(&property.second);
450 if (value == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600451 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700452 continue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600453 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700454 confData.serviceEnabled = *value;
455 break;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600456 }
457 }
458 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700459 else if (interface.first == ldapConfigInterfaceStr)
460 {
461
462 for (const auto& property : interface.second)
463 {
464 const std::string* strValue =
465 std::get_if<std::string>(&property.second);
466 if (strValue == nullptr)
467 {
468 continue;
469 }
470 if (property.first == "LDAPServerURI")
471 {
472 confData.uri = *strValue;
473 }
474 else if (property.first == "LDAPBindDN")
475 {
476 confData.bindDN = *strValue;
477 }
478 else if (property.first == "LDAPBaseDN")
479 {
480 confData.baseDN = *strValue;
481 }
482 else if (property.first == "LDAPSearchScope")
483 {
484 confData.searchScope = *strValue;
485 }
486 else if (property.first == "GroupNameAttribute")
487 {
488 confData.groupAttribute = *strValue;
489 }
490 else if (property.first == "UserNameAttribute")
491 {
492 confData.userNameAttribute = *strValue;
493 }
494 else if (property.first == "LDAPType")
495 {
496 confData.serverType = *strValue;
497 }
498 }
499 }
500 else if (interface.first ==
501 "xyz.openbmc_project.User.PrivilegeMapperEntry")
502 {
503 LDAPRoleMapData roleMapData{};
504 for (const auto& property : interface.second)
505 {
506 const std::string* strValue =
507 std::get_if<std::string>(&property.second);
508
509 if (strValue == nullptr)
510 {
511 continue;
512 }
513
514 if (property.first == "GroupName")
515 {
516 roleMapData.groupName = *strValue;
517 }
518 else if (property.first == "Privilege")
519 {
520 roleMapData.privilege = *strValue;
521 }
522 }
523
524 confData.groupRoleList.emplace_back(object.first.str,
525 roleMapData);
526 }
527 }
528 }
529 callback(true, confData, ldapType);
530 },
531 service, ldapRootObject, dbusObjManagerIntf, "GetManagedObjects");
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600532 },
533 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
Ed Tanous23a21a12020-07-25 04:45:05 +0000534 ldapConfigObjectName, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530535}
536
Ed Tanous6c51eab2021-06-03 12:30:29 -0700537/**
538 * @brief parses the authentication section under the LDAP
539 * @param input JSON data
540 * @param asyncResp pointer to the JSON response
541 * @param userName userName to be filled from the given JSON.
542 * @param password password to be filled from the given JSON.
543 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700544inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700545 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
546 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700547{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700548 std::optional<std::string> authType;
549
550 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
551 authType, "Username", username, "Password",
552 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700553 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700554 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700555 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700556 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530557 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700558 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530559 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700560 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530561 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700562 messages::propertyValueNotInList(asyncResp->res, *authType,
563 "AuthenticationType");
564 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530565 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700566}
567/**
568 * @brief parses the LDAPService section under the LDAP
569 * @param input JSON data
570 * @param asyncResp pointer to the JSON response
571 * @param baseDNList baseDN to be filled from the given JSON.
572 * @param userNameAttribute userName to be filled from the given JSON.
573 * @param groupaAttribute password to be filled from the given JSON.
574 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530575
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700576inline void
577 parseLDAPServiceJson(nlohmann::json input,
578 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
579 std::optional<std::vector<std::string>>& baseDNList,
580 std::optional<std::string>& userNameAttribute,
581 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700582{
583 std::optional<nlohmann::json> searchSettings;
584
585 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
586 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530587 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700588 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530589 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700590 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530591 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700592 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530593 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700594 if (!json_util::readJson(*searchSettings, asyncResp->res,
595 "BaseDistinguishedNames", baseDNList,
596 "UsernameAttribute", userNameAttribute,
597 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530598 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700599 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530600 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700601}
602/**
603 * @brief updates the LDAP server address and updates the
604 json response with the new value.
605 * @param serviceAddressList address to be updated.
606 * @param asyncResp pointer to the JSON response
607 * @param ldapServerElementName Type of LDAP
608 server(openLDAP/ActiveDirectory)
609 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530610
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700611inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700612 const std::vector<std::string>& serviceAddressList,
613 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
614 const std::string& ldapServerElementName,
615 const std::string& ldapConfigObject)
616{
617 crow::connections::systemBus->async_method_call(
618 [asyncResp, ldapServerElementName,
619 serviceAddressList](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700620 if (ec)
621 {
622 BMCWEB_LOG_DEBUG
623 << "Error Occurred in updating the service address";
624 messages::internalError(asyncResp->res);
625 return;
626 }
627 std::vector<std::string> modifiedserviceAddressList = {
628 serviceAddressList.front()};
629 asyncResp->res.jsonValue[ldapServerElementName]["ServiceAddresses"] =
630 modifiedserviceAddressList;
631 if ((serviceAddressList).size() > 1)
632 {
633 messages::propertyValueModified(asyncResp->res, "ServiceAddresses",
634 serviceAddressList.front());
635 }
636 BMCWEB_LOG_DEBUG << "Updated the service address";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700637 },
638 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
639 ldapConfigInterface, "LDAPServerURI",
Ed Tanous168e20c2021-12-13 14:39:53 -0800640 dbus::utility::DbusVariantType(serviceAddressList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700641}
642/**
643 * @brief updates the LDAP Bind DN and updates the
644 json response with the new value.
645 * @param username name of the user which needs to be updated.
646 * @param asyncResp pointer to the JSON response
647 * @param ldapServerElementName Type of LDAP
648 server(openLDAP/ActiveDirectory)
649 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530650
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700651inline void
652 handleUserNamePatch(const std::string& username,
653 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
654 const std::string& ldapServerElementName,
655 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700656{
657 crow::connections::systemBus->async_method_call(
658 [asyncResp, username,
659 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700660 if (ec)
661 {
662 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
663 messages::internalError(asyncResp->res);
664 return;
665 }
666 asyncResp->res
667 .jsonValue[ldapServerElementName]["Authentication"]["Username"] =
668 username;
669 BMCWEB_LOG_DEBUG << "Updated the username";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700670 },
671 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800672 ldapConfigInterface, "LDAPBindDN",
673 dbus::utility::DbusVariantType(username));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700674}
675
676/**
677 * @brief updates the LDAP password
678 * @param password : ldap password which needs to be updated.
679 * @param asyncResp pointer to the JSON response
680 * @param ldapServerElementName Type of LDAP
681 * server(openLDAP/ActiveDirectory)
682 */
683
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700684inline void
685 handlePasswordPatch(const std::string& password,
686 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
687 const std::string& ldapServerElementName,
688 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700689{
690 crow::connections::systemBus->async_method_call(
691 [asyncResp, password,
692 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700693 if (ec)
694 {
695 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
696 messages::internalError(asyncResp->res);
697 return;
698 }
699 asyncResp->res
700 .jsonValue[ldapServerElementName]["Authentication"]["Password"] =
701 "";
702 BMCWEB_LOG_DEBUG << "Updated the password";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700703 },
704 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
705 ldapConfigInterface, "LDAPBindDNPassword",
Ed Tanous168e20c2021-12-13 14:39:53 -0800706 dbus::utility::DbusVariantType(password));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700707}
708
709/**
710 * @brief updates the LDAP BaseDN and updates the
711 json response with the new value.
712 * @param baseDNList baseDN list which needs to be updated.
713 * @param asyncResp pointer to the JSON response
714 * @param ldapServerElementName Type of LDAP
715 server(openLDAP/ActiveDirectory)
716 */
717
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700718inline void
719 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
720 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
721 const std::string& ldapServerElementName,
722 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700723{
724 crow::connections::systemBus->async_method_call(
725 [asyncResp, baseDNList,
726 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700727 if (ec)
728 {
729 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
730 messages::internalError(asyncResp->res);
731 return;
732 }
733 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
734 auto& searchSettingsJson =
735 serverTypeJson["LDAPService"]["SearchSettings"];
736 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
737 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
738 if (baseDNList.size() > 1)
739 {
740 messages::propertyValueModified(
741 asyncResp->res, "BaseDistinguishedNames", baseDNList.front());
742 }
743 BMCWEB_LOG_DEBUG << "Updated the base DN";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700744 },
745 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
746 ldapConfigInterface, "LDAPBaseDN",
Ed Tanous168e20c2021-12-13 14:39:53 -0800747 dbus::utility::DbusVariantType(baseDNList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700748}
749/**
750 * @brief updates the LDAP user name attribute and updates the
751 json response with the new value.
752 * @param userNameAttribute attribute to be updated.
753 * @param asyncResp pointer to the JSON response
754 * @param ldapServerElementName Type of LDAP
755 server(openLDAP/ActiveDirectory)
756 */
757
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700758inline void
759 handleUserNameAttrPatch(const std::string& userNameAttribute,
760 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
761 const std::string& ldapServerElementName,
762 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700763{
764 crow::connections::systemBus->async_method_call(
765 [asyncResp, userNameAttribute,
766 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700767 if (ec)
768 {
769 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
770 "username attribute";
771 messages::internalError(asyncResp->res);
772 return;
773 }
774 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
775 auto& searchSettingsJson =
776 serverTypeJson["LDAPService"]["SearchSettings"];
777 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
778 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700779 },
780 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
781 ldapConfigInterface, "UserNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800782 dbus::utility::DbusVariantType(userNameAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700783}
784/**
785 * @brief updates the LDAP group attribute and updates the
786 json response with the new value.
787 * @param groupsAttribute attribute to be updated.
788 * @param asyncResp pointer to the JSON response
789 * @param ldapServerElementName Type of LDAP
790 server(openLDAP/ActiveDirectory)
791 */
792
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700793inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700794 const std::string& groupsAttribute,
795 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
796 const std::string& ldapServerElementName,
797 const std::string& ldapConfigObject)
798{
799 crow::connections::systemBus->async_method_call(
800 [asyncResp, groupsAttribute,
801 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700802 if (ec)
803 {
804 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
805 "groupname attribute";
806 messages::internalError(asyncResp->res);
807 return;
808 }
809 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
810 auto& searchSettingsJson =
811 serverTypeJson["LDAPService"]["SearchSettings"];
812 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
813 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700814 },
815 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
816 ldapConfigInterface, "GroupNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800817 dbus::utility::DbusVariantType(groupsAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700818}
819/**
820 * @brief updates the LDAP service enable and updates the
821 json response with the new value.
822 * @param input JSON data.
823 * @param asyncResp pointer to the JSON response
824 * @param ldapServerElementName Type of LDAP
825 server(openLDAP/ActiveDirectory)
826 */
827
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700828inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700829 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
830 const std::string& ldapServerElementName,
831 const std::string& ldapConfigObject)
832{
833 crow::connections::systemBus->async_method_call(
834 [asyncResp, serviceEnabled,
835 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700836 if (ec)
837 {
838 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the service enable";
839 messages::internalError(asyncResp->res);
840 return;
841 }
842 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
843 serviceEnabled;
844 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700845 },
846 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800847 ldapEnableInterface, "Enabled",
848 dbus::utility::DbusVariantType(serviceEnabled));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700849}
850
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700851inline void
852 handleAuthMethodsPatch(nlohmann::json& input,
853 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700854{
855 std::optional<bool> basicAuth;
856 std::optional<bool> cookie;
857 std::optional<bool> sessionToken;
858 std::optional<bool> xToken;
859 std::optional<bool> tls;
860
861 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
862 "Cookie", cookie, "SessionToken", sessionToken,
863 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530864 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700865 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
866 return;
867 }
868
869 // Make a copy of methods configuration
870 persistent_data::AuthConfigMethods authMethodsConfig =
871 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
872
873 if (basicAuth)
874 {
875#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
876 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800877 asyncResp->res,
878 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700879 return;
880#endif
881 authMethodsConfig.basic = *basicAuth;
882 }
883
884 if (cookie)
885 {
886#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800887 messages::actionNotSupported(
888 asyncResp->res,
889 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700890 return;
891#endif
892 authMethodsConfig.cookie = *cookie;
893 }
894
895 if (sessionToken)
896 {
897#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
898 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800899 asyncResp->res,
900 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700901 return;
902#endif
903 authMethodsConfig.sessionToken = *sessionToken;
904 }
905
906 if (xToken)
907 {
908#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800909 messages::actionNotSupported(
910 asyncResp->res,
911 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700912 return;
913#endif
914 authMethodsConfig.xtoken = *xToken;
915 }
916
917 if (tls)
918 {
919#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800920 messages::actionNotSupported(
921 asyncResp->res,
922 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700923 return;
924#endif
925 authMethodsConfig.tls = *tls;
926 }
927
928 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
929 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
930 !authMethodsConfig.tls)
931 {
932 // Do not allow user to disable everything
933 messages::actionNotSupported(asyncResp->res,
934 "of disabling all available methods");
935 return;
936 }
937
938 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
939 authMethodsConfig);
940 // Save configuration immediately
941 persistent_data::getConfig().writeData();
942
943 messages::success(asyncResp->res);
944}
945
946/**
947 * @brief Get the required values from the given JSON, validates the
948 * value and create the LDAP config object.
949 * @param input JSON data
950 * @param asyncResp pointer to the JSON response
951 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
952 */
953
954inline void handleLDAPPatch(nlohmann::json& input,
955 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
956 const std::string& serverType)
957{
958 std::string dbusObjectPath;
959 if (serverType == "ActiveDirectory")
960 {
961 dbusObjectPath = adConfigObject;
962 }
963 else if (serverType == "LDAP")
964 {
965 dbusObjectPath = ldapConfigObjectName;
966 }
967 else
968 {
969 return;
970 }
971
972 std::optional<nlohmann::json> authentication;
973 std::optional<nlohmann::json> ldapService;
974 std::optional<std::vector<std::string>> serviceAddressList;
975 std::optional<bool> serviceEnabled;
976 std::optional<std::vector<std::string>> baseDNList;
977 std::optional<std::string> userNameAttribute;
978 std::optional<std::string> groupsAttribute;
979 std::optional<std::string> userName;
980 std::optional<std::string> password;
981 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
982
983 if (!json_util::readJson(input, asyncResp->res, "Authentication",
984 authentication, "LDAPService", ldapService,
985 "ServiceAddresses", serviceAddressList,
986 "ServiceEnabled", serviceEnabled,
987 "RemoteRoleMapping", remoteRoleMapData))
988 {
989 return;
990 }
991
992 if (authentication)
993 {
994 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
995 password);
996 }
997 if (ldapService)
998 {
999 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1000 userNameAttribute, groupsAttribute);
1001 }
1002 if (serviceAddressList)
1003 {
Ed Tanous26f69762022-01-25 09:49:11 -08001004 if (serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301005 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001006 messages::propertyValueNotInList(asyncResp->res, "[]",
1007 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001008 return;
1009 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001010 }
1011 if (baseDNList)
1012 {
Ed Tanous26f69762022-01-25 09:49:11 -08001013 if (baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +05301014 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001015 messages::propertyValueNotInList(asyncResp->res, "[]",
1016 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301017 return;
1018 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001019 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301020
Ed Tanous6c51eab2021-06-03 12:30:29 -07001021 // nothing to update, then return
1022 if (!userName && !password && !serviceAddressList && !baseDNList &&
1023 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1024 !remoteRoleMapData)
1025 {
1026 return;
1027 }
1028
1029 // Get the existing resource first then keep modifying
1030 // whenever any property gets updated.
Ed Tanous002d39b2022-05-31 08:59:27 -07001031 getLDAPConfigData(
1032 serverType,
1033 [asyncResp, userName, password, baseDNList, userNameAttribute,
1034 groupsAttribute, serviceAddressList, serviceEnabled, dbusObjectPath,
1035 remoteRoleMapData](bool success, const LDAPConfigData& confData,
1036 const std::string& serverT) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001037 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301038 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001039 messages::internalError(asyncResp->res);
1040 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301041 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001042 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1043 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301044 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001045 // Disable the service first and update the rest of
1046 // the properties.
1047 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301048 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001049
Ratan Gupta8a07d282019-03-16 08:33:47 +05301050 if (serviceAddressList)
1051 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001052 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1053 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301054 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001055 if (userName)
1056 {
1057 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1058 }
1059 if (password)
1060 {
1061 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1062 }
1063
Ratan Gupta8a07d282019-03-16 08:33:47 +05301064 if (baseDNList)
1065 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001066 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1067 }
1068 if (userNameAttribute)
1069 {
1070 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1071 dbusObjectPath);
1072 }
1073 if (groupsAttribute)
1074 {
1075 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1076 dbusObjectPath);
1077 }
1078 if (serviceEnabled)
1079 {
1080 // if user has given the value as true then enable
1081 // the service. if user has given false then no-op
1082 // as service is already stopped.
1083 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301084 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001085 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1086 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301087 }
1088 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001089 else
1090 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001091 // if user has not given the service enabled value
1092 // then revert it to the same state as it was
1093 // before.
1094 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1095 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001096 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001097
Ed Tanous6c51eab2021-06-03 12:30:29 -07001098 if (remoteRoleMapData)
1099 {
1100 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1101 *remoteRoleMapData);
1102 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001103 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001104}
1105
1106inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1107 const std::string& username,
1108 std::optional<std::string> password,
1109 std::optional<bool> enabled,
1110 std::optional<std::string> roleId,
1111 std::optional<bool> locked)
1112{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301113 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1114 tempObjPath /= username;
1115 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001116
1117 dbus::utility::checkDbusPathExists(
1118 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001119 [dbusObjectPath, username, password(std::move(password)),
1120 roleId(std::move(roleId)), enabled, locked,
1121 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001122 if (rc <= 0)
1123 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001124 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
Ed Tanous002d39b2022-05-31 08:59:27 -07001125 username);
1126 return;
1127 }
1128
1129 if (password)
1130 {
1131 int retval = pamUpdatePassword(username, *password);
1132
1133 if (retval == PAM_USER_UNKNOWN)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001134 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001135 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1136 username);
Ed Tanous002d39b2022-05-31 08:59:27 -07001137 }
1138 else if (retval == PAM_AUTHTOK_ERR)
1139 {
1140 // If password is invalid
1141 messages::propertyValueFormatError(asyncResp->res, *password,
1142 "Password");
1143 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1144 }
1145 else if (retval != PAM_SUCCESS)
1146 {
1147 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001148 return;
1149 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001150 else
Ed Tanous6c51eab2021-06-03 12:30:29 -07001151 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001152 messages::success(asyncResp->res);
1153 }
1154 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001155
Ed Tanous002d39b2022-05-31 08:59:27 -07001156 if (enabled)
1157 {
1158 crow::connections::systemBus->async_method_call(
1159 [asyncResp](const boost::system::error_code ec) {
1160 if (ec)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001161 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001162 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001163 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001164 return;
1165 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001166 messages::success(asyncResp->res);
1167 return;
1168 },
1169 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1170 "org.freedesktop.DBus.Properties", "Set",
1171 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1172 dbus::utility::DbusVariantType{*enabled});
1173 }
1174
1175 if (roleId)
1176 {
1177 std::string priv = getPrivilegeFromRoleId(*roleId);
1178 if (priv.empty())
1179 {
1180 messages::propertyValueNotInList(asyncResp->res, *roleId,
1181 "RoleId");
1182 return;
1183 }
1184 if (priv == "priv-noaccess")
1185 {
1186 priv = "";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001187 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001188
Ed Tanous002d39b2022-05-31 08:59:27 -07001189 crow::connections::systemBus->async_method_call(
1190 [asyncResp](const boost::system::error_code ec) {
1191 if (ec)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001192 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001193 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1194 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001195 return;
1196 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001197 messages::success(asyncResp->res);
1198 },
1199 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1200 "org.freedesktop.DBus.Properties", "Set",
1201 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1202 dbus::utility::DbusVariantType{priv});
1203 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001204
Ed Tanous002d39b2022-05-31 08:59:27 -07001205 if (locked)
1206 {
1207 // admin can unlock the account which is locked by
1208 // successive authentication failures but admin should
1209 // not be allowed to lock an account.
1210 if (*locked)
1211 {
1212 messages::propertyValueNotInList(asyncResp->res, "true",
1213 "Locked");
1214 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001215 }
1216
Ed Tanous002d39b2022-05-31 08:59:27 -07001217 crow::connections::systemBus->async_method_call(
1218 [asyncResp](const boost::system::error_code ec) {
1219 if (ec)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001220 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001221 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1222 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001223 return;
1224 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001225 messages::success(asyncResp->res);
1226 return;
1227 },
1228 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1229 "org.freedesktop.DBus.Properties", "Set",
1230 "xyz.openbmc_project.User.Attributes",
1231 "UserLockedForFailedAttempt",
1232 dbus::utility::DbusVariantType{*locked});
1233 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001234 });
1235}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001236
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001237inline void handleAccountServiceHead(
1238 App& app, const crow::Request& req,
1239 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001240{
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001241
Ed Tanous1ef4c342022-05-12 16:12:36 -07001242 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1243 {
1244 return;
1245 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001246 asyncResp->res.addHeader(
1247 boost::beast::http::field::link,
1248 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1249}
1250
1251inline void
1252 handleAccountServiceGet(App& app, const crow::Request& req,
1253 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1254{
1255 handleAccountServiceHead(app, req, asyncResp);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001256 const persistent_data::AuthConfigMethods& authMethodsConfig =
1257 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1258
1259 nlohmann::json& json = asyncResp->res.jsonValue;
1260 json["@odata.id"] = "/redfish/v1/AccountService";
1261 json["@odata.type"] = "#AccountService."
1262 "v1_10_0.AccountService";
1263 json["Id"] = "AccountService";
1264 json["Name"] = "Account Service";
1265 json["Description"] = "Account Service";
1266 json["ServiceEnabled"] = true;
1267 json["MaxPasswordLength"] = 20;
1268 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1269 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
1270 json["Oem"]["OpenBMC"]["@odata.type"] =
1271 "#OemAccountService.v1_0_0.AccountService";
1272 json["Oem"]["OpenBMC"]["@odata.id"] =
1273 "/redfish/v1/AccountService#/Oem/OpenBMC";
1274 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1275 authMethodsConfig.basic;
1276 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1277 authMethodsConfig.sessionToken;
1278 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1279 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1280 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1281
1282 // /redfish/v1/AccountService/LDAP/Certificates is something only
1283 // ConfigureManager can access then only display when the user has
1284 // permissions ConfigureManager
1285 Privileges effectiveUserPrivileges =
1286 redfish::getUserPrivileges(req.userRole);
1287
1288 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1289 effectiveUserPrivileges))
1290 {
1291 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1292 "/redfish/v1/AccountService/LDAP/Certificates";
1293 }
1294 crow::connections::systemBus->async_method_call(
1295 [asyncResp](const boost::system::error_code ec,
1296 const dbus::utility::DBusPropertiesMap& propertiesList) {
1297 if (ec)
1298 {
1299 messages::internalError(asyncResp->res);
1300 return;
1301 }
1302 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1303 << "properties for AccountService";
1304 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
1305 property : propertiesList)
1306 {
1307 if (property.first == "MinPasswordLength")
1308 {
1309 const uint8_t* value = std::get_if<uint8_t>(&property.second);
1310 if (value != nullptr)
1311 {
1312 asyncResp->res.jsonValue["MinPasswordLength"] = *value;
1313 }
1314 }
1315 if (property.first == "AccountUnlockTimeout")
1316 {
1317 const uint32_t* value = std::get_if<uint32_t>(&property.second);
1318 if (value != nullptr)
1319 {
1320 asyncResp->res.jsonValue["AccountLockoutDuration"] = *value;
1321 }
1322 }
1323 if (property.first == "MaxLoginAttemptBeforeLockout")
1324 {
1325 const uint16_t* value = std::get_if<uint16_t>(&property.second);
1326 if (value != nullptr)
1327 {
1328 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1329 *value;
1330 }
1331 }
1332 }
1333 },
1334 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1335 "org.freedesktop.DBus.Properties", "GetAll",
1336 "xyz.openbmc_project.User.AccountPolicy");
1337
Ed Tanous02cad962022-06-30 16:50:15 -07001338 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001339 const std::string& ldapType) {
1340 if (!success)
1341 {
1342 return;
1343 }
1344 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1345 };
1346
1347 getLDAPConfigData("LDAP", callback);
1348 getLDAPConfigData("ActiveDirectory", callback);
1349}
1350
1351inline void handleAccountServicePatch(
1352 App& app, const crow::Request& req,
1353 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1354{
1355 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1356 {
1357 return;
1358 }
1359 std::optional<uint32_t> unlockTimeout;
1360 std::optional<uint16_t> lockoutThreshold;
1361 std::optional<uint8_t> minPasswordLength;
1362 std::optional<uint16_t> maxPasswordLength;
1363 std::optional<nlohmann::json> ldapObject;
1364 std::optional<nlohmann::json> activeDirectoryObject;
1365 std::optional<nlohmann::json> oemObject;
1366
1367 if (!json_util::readJsonPatch(
1368 req, asyncResp->res, "AccountLockoutDuration", unlockTimeout,
1369 "AccountLockoutThreshold", lockoutThreshold, "MaxPasswordLength",
1370 maxPasswordLength, "MinPasswordLength", minPasswordLength, "LDAP",
1371 ldapObject, "ActiveDirectory", activeDirectoryObject, "Oem",
1372 oemObject))
1373 {
1374 return;
1375 }
1376
1377 if (minPasswordLength)
1378 {
1379 crow::connections::systemBus->async_method_call(
1380 [asyncResp](const boost::system::error_code ec) {
1381 if (ec)
1382 {
1383 messages::internalError(asyncResp->res);
1384 return;
1385 }
1386 messages::success(asyncResp->res);
1387 },
1388 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1389 "org.freedesktop.DBus.Properties", "Set",
1390 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
1391 dbus::utility::DbusVariantType(*minPasswordLength));
1392 }
1393
1394 if (maxPasswordLength)
1395 {
1396 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1397 }
1398
1399 if (ldapObject)
1400 {
1401 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1402 }
1403
1404 if (std::optional<nlohmann::json> oemOpenBMCObject;
1405 oemObject && json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1406 oemOpenBMCObject))
1407 {
1408 if (std::optional<nlohmann::json> authMethodsObject;
1409 oemOpenBMCObject &&
1410 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1411 "AuthMethods", authMethodsObject))
1412 {
1413 if (authMethodsObject)
1414 {
1415 handleAuthMethodsPatch(*authMethodsObject, asyncResp);
1416 }
1417 }
1418 }
1419
1420 if (activeDirectoryObject)
1421 {
1422 handleLDAPPatch(*activeDirectoryObject, asyncResp, "ActiveDirectory");
1423 }
1424
1425 if (unlockTimeout)
1426 {
1427 crow::connections::systemBus->async_method_call(
1428 [asyncResp](const boost::system::error_code ec) {
1429 if (ec)
1430 {
1431 messages::internalError(asyncResp->res);
1432 return;
1433 }
1434 messages::success(asyncResp->res);
1435 },
1436 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1437 "org.freedesktop.DBus.Properties", "Set",
1438 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
1439 dbus::utility::DbusVariantType(*unlockTimeout));
1440 }
1441 if (lockoutThreshold)
1442 {
1443 crow::connections::systemBus->async_method_call(
1444 [asyncResp](const boost::system::error_code ec) {
1445 if (ec)
1446 {
1447 messages::internalError(asyncResp->res);
1448 return;
1449 }
1450 messages::success(asyncResp->res);
1451 },
1452 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1453 "org.freedesktop.DBus.Properties", "Set",
1454 "xyz.openbmc_project.User.AccountPolicy",
1455 "MaxLoginAttemptBeforeLockout",
1456 dbus::utility::DbusVariantType(*lockoutThreshold));
1457 }
1458}
1459
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001460inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001461 App& app, const crow::Request& req,
1462 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1463{
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001464
Ed Tanous1ef4c342022-05-12 16:12:36 -07001465 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1466 {
1467 return;
1468 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001469 asyncResp->res.addHeader(
1470 boost::beast::http::field::link,
1471 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1472}
1473
1474inline void handleAccountCollectionGet(
1475 App& app, const crow::Request& req,
1476 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1477{
1478 handleAccountCollectionHead(app, req, asyncResp);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001479
1480 asyncResp->res.jsonValue["@odata.id"] =
1481 "/redfish/v1/AccountService/Accounts";
1482 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1483 "ManagerAccountCollection";
1484 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1485 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1486
1487 Privileges effectiveUserPrivileges =
1488 redfish::getUserPrivileges(req.userRole);
1489
1490 std::string thisUser;
1491 if (req.session)
1492 {
1493 thisUser = req.session->username;
1494 }
1495 crow::connections::systemBus->async_method_call(
1496 [asyncResp, thisUser, effectiveUserPrivileges](
1497 const boost::system::error_code ec,
1498 const dbus::utility::ManagedObjectType& users) {
1499 if (ec)
1500 {
1501 messages::internalError(asyncResp->res);
1502 return;
1503 }
1504
1505 bool userCanSeeAllAccounts =
1506 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1507
1508 bool userCanSeeSelf =
1509 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1510
1511 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1512 memberArray = nlohmann::json::array();
1513
1514 for (const auto& userpath : users)
1515 {
1516 std::string user = userpath.first.filename();
1517 if (user.empty())
1518 {
1519 messages::internalError(asyncResp->res);
1520 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1521
1522 return;
1523 }
1524
1525 // As clarified by Redfish here:
1526 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1527 // Users without ConfigureUsers, only see their own
1528 // account. Users with ConfigureUsers, see all
1529 // accounts.
1530 if (userCanSeeAllAccounts || (thisUser == user && userCanSeeSelf))
1531 {
1532 nlohmann::json::object_t member;
1533 member["@odata.id"] =
1534 "/redfish/v1/AccountService/Accounts/" + user;
1535 memberArray.push_back(std::move(member));
1536 }
1537 }
1538 asyncResp->res.jsonValue["Members@odata.count"] = memberArray.size();
1539 },
1540 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1541 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1542}
1543
1544inline void handleAccountCollectionPost(
1545 App& app, const crow::Request& req,
1546 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1547{
1548 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1549 {
1550 return;
1551 }
1552 std::string username;
1553 std::string password;
1554 std::optional<std::string> roleId("User");
1555 std::optional<bool> enabled = true;
1556 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", username,
1557 "Password", password, "RoleId", roleId,
1558 "Enabled", enabled))
1559 {
1560 return;
1561 }
1562
1563 std::string priv = getPrivilegeFromRoleId(*roleId);
1564 if (priv.empty())
1565 {
1566 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
1567 return;
1568 }
1569 // TODO: Following override will be reverted once support in
1570 // phosphor-user-manager is added. In order to avoid dependency
1571 // issues, this is added in bmcweb, which will removed, once
1572 // phosphor-user-manager supports priv-noaccess.
1573 if (priv == "priv-noaccess")
1574 {
1575 roleId = "";
1576 }
1577 else
1578 {
1579 roleId = priv;
1580 }
1581
1582 // Reading AllGroups property
1583 sdbusplus::asio::getProperty<std::vector<std::string>>(
1584 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1585 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager",
1586 "AllGroups",
1587 [asyncResp, username, password{std::move(password)}, roleId,
1588 enabled](const boost::system::error_code ec,
1589 const std::vector<std::string>& allGroupsList) {
1590 if (ec)
1591 {
1592 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1593 messages::internalError(asyncResp->res);
1594 return;
1595 }
1596
1597 if (allGroupsList.empty())
1598 {
1599 messages::internalError(asyncResp->res);
1600 return;
1601 }
1602
1603 crow::connections::systemBus->async_method_call(
1604 [asyncResp, username, password](const boost::system::error_code ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -05001605 sdbusplus::message_t& m) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001606 if (ec2)
1607 {
1608 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1609 return;
1610 }
1611
1612 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1613 {
1614 // At this point we have a user that's been
1615 // created, but the password set
1616 // failed.Something is wrong, so delete the user
1617 // that we've already created
1618 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1619 tempObjPath /= username;
1620 const std::string userPath(tempObjPath);
1621
1622 crow::connections::systemBus->async_method_call(
1623 [asyncResp, password](const boost::system::error_code ec3) {
1624 if (ec3)
1625 {
1626 messages::internalError(asyncResp->res);
1627 return;
1628 }
1629
1630 // If password is invalid
1631 messages::propertyValueFormatError(asyncResp->res, password,
1632 "Password");
1633 },
1634 "xyz.openbmc_project.User.Manager", userPath,
1635 "xyz.openbmc_project.Object.Delete", "Delete");
1636
1637 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1638 return;
1639 }
1640
1641 messages::created(asyncResp->res);
1642 asyncResp->res.addHeader(
1643 "Location", "/redfish/v1/AccountService/Accounts/" + username);
1644 },
1645 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1646 "xyz.openbmc_project.User.Manager", "CreateUser", username,
1647 allGroupsList, *roleId, *enabled);
1648 });
1649}
1650
1651inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001652 handleAccountHead(App& app, const crow::Request& req,
1653 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1654 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001655{
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001656
Ed Tanous1ef4c342022-05-12 16:12:36 -07001657 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1658 {
1659 return;
1660 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001661 asyncResp->res.addHeader(
1662 boost::beast::http::field::link,
1663 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1664}
1665inline void
1666 handleAccountGet(App& app, const crow::Request& req,
1667 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1668 const std::string& accountName)
1669{
1670 handleAccountHead(app, req, asyncResp, accountName);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001671#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1672 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001673 messages::resourceNotFound(asyncResp->res, "ManagerAccount", accountName);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001674 return;
1675
1676#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1677 if (req.session == nullptr)
1678 {
1679 messages::internalError(asyncResp->res);
1680 return;
1681 }
1682 if (req.session->username != accountName)
1683 {
1684 // At this point we've determined that the user is trying to
1685 // modify a user that isn't them. We need to verify that they
1686 // have permissions to modify other users, so re-run the auth
1687 // check with the same permissions, minus ConfigureSelf.
1688 Privileges effectiveUserPrivileges =
1689 redfish::getUserPrivileges(req.userRole);
1690 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
1691 "ConfigureManager"};
1692 if (!effectiveUserPrivileges.isSupersetOf(
1693 requiredPermissionsToChangeNonSelf))
1694 {
1695 BMCWEB_LOG_DEBUG << "GET Account denied access";
1696 messages::insufficientPrivilege(asyncResp->res);
1697 return;
1698 }
1699 }
1700
1701 crow::connections::systemBus->async_method_call(
1702 [asyncResp,
1703 accountName](const boost::system::error_code ec,
1704 const dbus::utility::ManagedObjectType& users) {
1705 if (ec)
1706 {
1707 messages::internalError(asyncResp->res);
1708 return;
1709 }
1710 const auto userIt = std::find_if(
1711 users.begin(), users.end(),
1712 [accountName](
1713 const std::pair<sdbusplus::message::object_path,
1714 dbus::utility::DBusInteracesMap>& user) {
1715 return accountName == user.first.filename();
1716 });
1717
1718 if (userIt == users.end())
1719 {
1720 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1721 accountName);
1722 return;
1723 }
1724
1725 asyncResp->res.jsonValue["@odata.type"] =
1726 "#ManagerAccount.v1_4_0.ManagerAccount";
1727 asyncResp->res.jsonValue["Name"] = "User Account";
1728 asyncResp->res.jsonValue["Description"] = "User Account";
1729 asyncResp->res.jsonValue["Password"] = nullptr;
1730 asyncResp->res.jsonValue["AccountTypes"] = {"Redfish"};
1731
1732 for (const auto& interface : userIt->second)
1733 {
1734 if (interface.first == "xyz.openbmc_project.User.Attributes")
1735 {
1736 for (const auto& property : interface.second)
1737 {
1738 if (property.first == "UserEnabled")
1739 {
1740 const bool* userEnabled =
1741 std::get_if<bool>(&property.second);
1742 if (userEnabled == nullptr)
1743 {
1744 BMCWEB_LOG_ERROR << "UserEnabled wasn't a bool";
1745 messages::internalError(asyncResp->res);
1746 return;
1747 }
1748 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
1749 }
1750 else if (property.first == "UserLockedForFailedAttempt")
1751 {
1752 const bool* userLocked =
1753 std::get_if<bool>(&property.second);
1754 if (userLocked == nullptr)
1755 {
1756 BMCWEB_LOG_ERROR << "UserLockedForF"
1757 "ailedAttempt "
1758 "wasn't a bool";
1759 messages::internalError(asyncResp->res);
1760 return;
1761 }
1762 asyncResp->res.jsonValue["Locked"] = *userLocked;
1763 asyncResp->res
1764 .jsonValue["Locked@Redfish.AllowableValues"] = {
1765 "false"}; // can only unlock accounts
1766 }
1767 else if (property.first == "UserPrivilege")
1768 {
1769 const std::string* userPrivPtr =
1770 std::get_if<std::string>(&property.second);
1771 if (userPrivPtr == nullptr)
1772 {
1773 BMCWEB_LOG_ERROR << "UserPrivilege wasn't a "
1774 "string";
1775 messages::internalError(asyncResp->res);
1776 return;
1777 }
1778 std::string role = getRoleIdFromPrivilege(*userPrivPtr);
1779 if (role.empty())
1780 {
1781 BMCWEB_LOG_ERROR << "Invalid user role";
1782 messages::internalError(asyncResp->res);
1783 return;
1784 }
1785 asyncResp->res.jsonValue["RoleId"] = role;
1786
1787 nlohmann::json& roleEntry =
1788 asyncResp->res.jsonValue["Links"]["Role"];
1789 roleEntry["@odata.id"] =
1790 "/redfish/v1/AccountService/Roles/" + role;
1791 }
1792 else if (property.first == "UserPasswordExpired")
1793 {
1794 const bool* userPasswordExpired =
1795 std::get_if<bool>(&property.second);
1796 if (userPasswordExpired == nullptr)
1797 {
1798 BMCWEB_LOG_ERROR
1799 << "UserPasswordExpired wasn't a bool";
1800 messages::internalError(asyncResp->res);
1801 return;
1802 }
1803 asyncResp->res.jsonValue["PasswordChangeRequired"] =
1804 *userPasswordExpired;
1805 }
1806 }
1807 }
1808 }
1809
1810 asyncResp->res.jsonValue["@odata.id"] =
1811 "/redfish/v1/AccountService/Accounts/" + accountName;
1812 asyncResp->res.jsonValue["Id"] = accountName;
1813 asyncResp->res.jsonValue["UserName"] = accountName;
1814 },
1815 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1816 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1817}
1818
1819inline void
1820 handleAccounttDelete(App& app, const crow::Request& req,
1821 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1822 const std::string& username)
1823{
1824 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1825 {
1826 return;
1827 }
1828
1829#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1830 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001831 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001832 return;
1833
1834#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1835 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1836 tempObjPath /= username;
1837 const std::string userPath(tempObjPath);
1838
1839 crow::connections::systemBus->async_method_call(
1840 [asyncResp, username](const boost::system::error_code ec) {
1841 if (ec)
1842 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001843 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
Ed Tanous1ef4c342022-05-12 16:12:36 -07001844 username);
1845 return;
1846 }
1847
1848 messages::accountRemoved(asyncResp->res);
1849 },
1850 "xyz.openbmc_project.User.Manager", userPath,
1851 "xyz.openbmc_project.Object.Delete", "Delete");
1852}
1853
1854inline void
1855 handleAccountPatch(App& app, const crow::Request& req,
1856 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1857 const std::string& username)
1858{
1859 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1860 {
1861 return;
1862 }
1863#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1864 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001865 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001866 return;
1867
1868#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1869 std::optional<std::string> newUserName;
1870 std::optional<std::string> password;
1871 std::optional<bool> enabled;
1872 std::optional<std::string> roleId;
1873 std::optional<bool> locked;
1874
1875 if (req.session == nullptr)
1876 {
1877 messages::internalError(asyncResp->res);
1878 return;
1879 }
1880
1881 Privileges effectiveUserPrivileges =
1882 redfish::getUserPrivileges(req.userRole);
1883 Privileges configureUsers = {"ConfigureUsers"};
1884 bool userHasConfigureUsers =
1885 effectiveUserPrivileges.isSupersetOf(configureUsers);
1886 if (userHasConfigureUsers)
1887 {
1888 // Users with ConfigureUsers can modify for all users
1889 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
1890 newUserName, "Password", password,
1891 "RoleId", roleId, "Enabled", enabled,
1892 "Locked", locked))
1893 {
1894 return;
1895 }
1896 }
1897 else
1898 {
1899 // ConfigureSelf accounts can only modify their own account
1900 if (username != req.session->username)
1901 {
1902 messages::insufficientPrivilege(asyncResp->res);
1903 return;
1904 }
1905
1906 // ConfigureSelf accounts can only modify their password
1907 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
1908 password))
1909 {
1910 return;
1911 }
1912 }
1913
1914 // if user name is not provided in the patch method or if it
1915 // matches the user name in the URI, then we are treating it as
1916 // updating user properties other then username. If username
1917 // provided doesn't match the URI, then we are treating this as
1918 // user rename request.
1919 if (!newUserName || (newUserName.value() == username))
1920 {
1921 updateUserProperties(asyncResp, username, password, enabled, roleId,
1922 locked);
1923 return;
1924 }
1925 crow::connections::systemBus->async_method_call(
1926 [asyncResp, username, password(std::move(password)),
1927 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Patrick Williams59d494e2022-07-22 19:26:55 -05001928 locked](const boost::system::error_code ec, sdbusplus::message_t& m) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001929 if (ec)
1930 {
1931 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
1932 username);
1933 return;
1934 }
1935
1936 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
1937 locked);
1938 },
1939 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1940 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1941 *newUserName);
1942}
1943
Ed Tanous6c51eab2021-06-03 12:30:29 -07001944inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001945{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001946
Ed Tanous6c51eab2021-06-03 12:30:29 -07001947 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001948 .privileges(redfish::privileges::headAccountService)
1949 .methods(boost::beast::http::verb::head)(
1950 std::bind_front(handleAccountServiceHead, std::ref(app)));
1951
1952 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001953 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07001954 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001955 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001956
Ed Tanousf5ffd802021-07-19 10:55:33 -07001957 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06001958 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07001959 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001960 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001961
Ed Tanous6c51eab2021-06-03 12:30:29 -07001962 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001963 .privileges(redfish::privileges::headManagerAccountCollection)
1964 .methods(boost::beast::http::verb::head)(
1965 std::bind_front(handleAccountCollectionHead, std::ref(app)));
1966
1967 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001968 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001969 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001970 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07001971
Ed Tanous6c51eab2021-06-03 12:30:29 -07001972 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001973 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001974 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001975 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07001976
1977 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001978 .privileges(redfish::privileges::headManagerAccount)
1979 .methods(boost::beast::http::verb::head)(
1980 std::bind_front(handleAccountHead, std::ref(app)));
1981
1982 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07001983 .privileges(redfish::privileges::getManagerAccount)
1984 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001985 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001986
1987 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001988 // TODO this privilege should be using the generated endpoints, but
1989 // because of the special handling of ConfigureSelf, it's not able to
1990 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001991 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1992 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001993 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001994
1995 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001996 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001997 .methods(boost::beast::http::verb::delete_)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001998 std::bind_front(handleAccounttDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001999}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002000
Ed Tanous1abe55e2018-09-05 08:30:59 -07002001} // namespace redfish