blob: d7c70c1cb1c0d9753c77cd20ea7ec8c0b4f2819b [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 {
126 messages::resourceAlreadyExists(asyncResp->res,
Gunnar Mills8114bd42020-06-11 20:55:21 -0500127 "#ManagerAccount.v1_4_0.ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000128 "UserName", newUser);
129 }
130 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
131 "UserNameDoesNotExist") == 0)
132 {
133 messages::resourceNotFound(
Gunnar Mills8114bd42020-06-11 20:55:21 -0500134 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000135 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700136 else if ((strcmp(errorMessage,
137 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
138 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800139 (strcmp(
140 errorMessage,
141 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
142 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000143 {
144 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
145 }
146 else if (strcmp(errorMessage,
147 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
148 {
149 messages::createLimitReachedForResource(asyncResp->res);
150 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000151 else
152 {
153 messages::internalError(asyncResp->res);
154 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000155}
156
Ed Tanous81ce6092020-12-17 16:54:55 +0000157inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000158 const LDAPConfigData& confData,
159 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530160{
Ratan Guptaab828d72019-04-22 14:18:33 +0530161 std::string service =
162 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600163
Ed Tanous14766872022-03-15 10:44:42 -0700164 nlohmann::json& ldap = jsonResponse[ldapType];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600165
Ed Tanous14766872022-03-15 10:44:42 -0700166 ldap["ServiceEnabled"] = confData.serviceEnabled;
167 ldap["ServiceAddresses"] = nlohmann::json::array({confData.uri});
168 ldap["Authentication"]["AuthenticationType"] = "UsernameAndPassword";
169 ldap["Authentication"]["Username"] = confData.bindDN;
170 ldap["Authentication"]["Password"] = nullptr;
171
172 ldap["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"] =
173 nlohmann::json::array({confData.baseDN});
174 ldap["LDAPService"]["SearchSettings"]["UsernameAttribute"] =
175 confData.userNameAttribute;
176 ldap["LDAPService"]["SearchSettings"]["GroupsAttribute"] =
177 confData.groupAttribute;
178
179 nlohmann::json& roleMapArray = ldap["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600180 roleMapArray = nlohmann::json::array();
Ed Tanous9eb808c2022-01-25 10:19:23 -0800181 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600182 {
183 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
184 << obj.second.groupName << "\n";
185 roleMapArray.push_back(
186 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
187 nlohmann::json::array(
188 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
189 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530190}
191
192/**
Ratan Gupta06785242019-07-26 22:30:16 +0530193 * @brief validates given JSON input and then calls appropriate method to
194 * create, to delete or to set Rolemapping object based on the given input.
195 *
196 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000197inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800198 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530199 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700200 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530201{
202 for (size_t index = 0; index < input.size(); index++)
203 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700204 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530205
206 if (thisJson.is_null())
207 {
208 // delete the existing object
209 if (index < roleMapObjData.size())
210 {
211 crow::connections::systemBus->async_method_call(
212 [asyncResp, roleMapObjData, serverType,
213 index](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700214 if (ec)
215 {
216 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
217 messages::internalError(asyncResp->res);
218 return;
219 }
220 asyncResp->res
221 .jsonValue[serverType]["RemoteRoleMapping"][index] =
222 nullptr;
Ratan Gupta06785242019-07-26 22:30:16 +0530223 },
224 ldapDbusService, roleMapObjData[index].first,
225 "xyz.openbmc_project.Object.Delete", "Delete");
226 }
227 else
228 {
229 BMCWEB_LOG_ERROR << "Can't delete the object";
230 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800231 asyncResp->res,
232 thisJson.dump(2, ' ', true,
233 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530234 "RemoteRoleMapping/" + std::to_string(index));
235 return;
236 }
237 }
238 else if (thisJson.empty())
239 {
240 // Don't do anything for the empty objects,parse next json
241 // eg {"RemoteRoleMapping",[{}]}
242 }
243 else
244 {
245 // update/create the object
246 std::optional<std::string> remoteGroup;
247 std::optional<std::string> localRole;
248
Ed Tanousf23b7292020-10-15 09:41:17 -0700249 // This is a copy, but it's required in this case because of how
250 // readJson is structured
251 nlohmann::json thisJsonCopy = thisJson;
252 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
253 "RemoteGroup", remoteGroup, "LocalRole",
254 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530255 {
256 continue;
257 }
258
259 // Update existing RoleMapping Object
260 if (index < roleMapObjData.size())
261 {
262 BMCWEB_LOG_DEBUG << "Update Role Map Object";
263 // If "RemoteGroup" info is provided
264 if (remoteGroup)
265 {
266 crow::connections::systemBus->async_method_call(
267 [asyncResp, roleMapObjData, serverType, index,
268 remoteGroup](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700269 if (ec)
270 {
271 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
272 messages::internalError(asyncResp->res);
273 return;
274 }
275 asyncResp->res
276 .jsonValue[serverType]["RemoteRoleMapping"][index]
277 ["RemoteGroup"] = *remoteGroup;
Ratan Gupta06785242019-07-26 22:30:16 +0530278 },
279 ldapDbusService, roleMapObjData[index].first,
280 propertyInterface, "Set",
281 "xyz.openbmc_project.User.PrivilegeMapperEntry",
282 "GroupName",
Ed Tanous168e20c2021-12-13 14:39:53 -0800283 dbus::utility::DbusVariantType(
284 std::move(*remoteGroup)));
Ratan Gupta06785242019-07-26 22:30:16 +0530285 }
286
287 // If "LocalRole" info is provided
288 if (localRole)
289 {
290 crow::connections::systemBus->async_method_call(
291 [asyncResp, roleMapObjData, serverType, index,
292 localRole](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700293 if (ec)
294 {
295 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
296 messages::internalError(asyncResp->res);
297 return;
298 }
299 asyncResp->res
300 .jsonValue[serverType]["RemoteRoleMapping"][index]
301 ["LocalRole"] = *localRole;
Ratan Gupta06785242019-07-26 22:30:16 +0530302 },
303 ldapDbusService, roleMapObjData[index].first,
304 propertyInterface, "Set",
305 "xyz.openbmc_project.User.PrivilegeMapperEntry",
306 "Privilege",
Ed Tanous168e20c2021-12-13 14:39:53 -0800307 dbus::utility::DbusVariantType(
Ratan Gupta06785242019-07-26 22:30:16 +0530308 getPrivilegeFromRoleId(std::move(*localRole))));
309 }
310 }
311 // Create a new RoleMapping Object.
312 else
313 {
314 BMCWEB_LOG_DEBUG
315 << "setRoleMappingProperties: Creating new Object";
316 std::string pathString =
317 "RemoteRoleMapping/" + std::to_string(index);
318
319 if (!localRole)
320 {
321 messages::propertyMissing(asyncResp->res,
322 pathString + "/LocalRole");
323 continue;
324 }
325 if (!remoteGroup)
326 {
327 messages::propertyMissing(asyncResp->res,
328 pathString + "/RemoteGroup");
329 continue;
330 }
331
332 std::string dbusObjectPath;
333 if (serverType == "ActiveDirectory")
334 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700335 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530336 }
337 else if (serverType == "LDAP")
338 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000339 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530340 }
341
342 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
343 << ",LocalRole=" << *localRole;
344
345 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700346 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530347 remoteGroup](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700348 if (ec)
349 {
350 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
351 messages::internalError(asyncResp->res);
352 return;
353 }
354 nlohmann::json& remoteRoleJson =
355 asyncResp->res
356 .jsonValue[serverType]["RemoteRoleMapping"];
357 nlohmann::json::object_t roleMapEntry;
358 roleMapEntry["LocalRole"] = *localRole;
359 roleMapEntry["RemoteGroup"] = *remoteGroup;
360 remoteRoleJson.push_back(std::move(roleMapEntry));
Ratan Gupta06785242019-07-26 22:30:16 +0530361 },
362 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700363 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530364 getPrivilegeFromRoleId(std::move(*localRole)));
365 }
366 }
367 }
368}
369
370/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530371 * Function that retrieves all properties for LDAP config object
372 * into JSON
373 */
374template <typename CallbackFunc>
375inline void getLDAPConfigData(const std::string& ldapType,
376 CallbackFunc&& callback)
377{
Ratan Guptaab828d72019-04-22 14:18:33 +0530378
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600379 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530380 ldapConfigInterface};
381
382 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600383 [callback, ldapType](const boost::system::error_code ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800384 const dbus::utility::MapperGetObject& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700385 if (ec || resp.empty())
386 {
387 BMCWEB_LOG_ERROR
388 << "DBUS response error during getting of service name: " << ec;
389 LDAPConfigData empty{};
390 callback(false, empty, ldapType);
391 return;
392 }
393 std::string service = resp.begin()->first;
394 crow::connections::systemBus->async_method_call(
395 [callback,
396 ldapType](const boost::system::error_code errorCode,
397 const dbus::utility::ManagedObjectType& ldapObjects) {
398 LDAPConfigData confData{};
399 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600400 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700401 callback(false, confData, ldapType);
402 BMCWEB_LOG_ERROR << "D-Bus responses error: " << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600403 return;
404 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600405
Ed Tanous002d39b2022-05-31 08:59:27 -0700406 std::string ldapDbusType;
407 std::string searchString;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600408
Ed Tanous002d39b2022-05-31 08:59:27 -0700409 if (ldapType == "LDAP")
410 {
411 ldapDbusType =
412 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
413 searchString = "openldap";
414 }
415 else if (ldapType == "ActiveDirectory")
416 {
417 ldapDbusType =
418 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
419 searchString = "active_directory";
420 }
421 else
422 {
423 BMCWEB_LOG_ERROR << "Can't get the DbusType for the given type="
424 << ldapType;
425 callback(false, confData, ldapType);
426 return;
427 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600428
Ed Tanous002d39b2022-05-31 08:59:27 -0700429 std::string ldapEnableInterfaceStr = ldapEnableInterface;
430 std::string ldapConfigInterfaceStr = ldapConfigInterface;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600431
Ed Tanous002d39b2022-05-31 08:59:27 -0700432 for (const auto& object : ldapObjects)
433 {
434 // let's find the object whose ldap type is equal to the
435 // given type
436 if (object.first.str.find(searchString) == std::string::npos)
437 {
438 continue;
439 }
440
441 for (const auto& interface : object.second)
442 {
443 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600444 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700445 // rest of the properties are string.
446 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600447 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700448 if (property.first == "Enabled")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600449 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700450 const bool* value =
451 std::get_if<bool>(&property.second);
452 if (value == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600453 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700454 continue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600455 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700456 confData.serviceEnabled = *value;
457 break;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600458 }
459 }
460 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700461 else if (interface.first == ldapConfigInterfaceStr)
462 {
463
464 for (const auto& property : interface.second)
465 {
466 const std::string* strValue =
467 std::get_if<std::string>(&property.second);
468 if (strValue == nullptr)
469 {
470 continue;
471 }
472 if (property.first == "LDAPServerURI")
473 {
474 confData.uri = *strValue;
475 }
476 else if (property.first == "LDAPBindDN")
477 {
478 confData.bindDN = *strValue;
479 }
480 else if (property.first == "LDAPBaseDN")
481 {
482 confData.baseDN = *strValue;
483 }
484 else if (property.first == "LDAPSearchScope")
485 {
486 confData.searchScope = *strValue;
487 }
488 else if (property.first == "GroupNameAttribute")
489 {
490 confData.groupAttribute = *strValue;
491 }
492 else if (property.first == "UserNameAttribute")
493 {
494 confData.userNameAttribute = *strValue;
495 }
496 else if (property.first == "LDAPType")
497 {
498 confData.serverType = *strValue;
499 }
500 }
501 }
502 else if (interface.first ==
503 "xyz.openbmc_project.User.PrivilegeMapperEntry")
504 {
505 LDAPRoleMapData roleMapData{};
506 for (const auto& property : interface.second)
507 {
508 const std::string* strValue =
509 std::get_if<std::string>(&property.second);
510
511 if (strValue == nullptr)
512 {
513 continue;
514 }
515
516 if (property.first == "GroupName")
517 {
518 roleMapData.groupName = *strValue;
519 }
520 else if (property.first == "Privilege")
521 {
522 roleMapData.privilege = *strValue;
523 }
524 }
525
526 confData.groupRoleList.emplace_back(object.first.str,
527 roleMapData);
528 }
529 }
530 }
531 callback(true, confData, ldapType);
532 },
533 service, ldapRootObject, dbusObjManagerIntf, "GetManagedObjects");
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600534 },
535 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
Ed Tanous23a21a12020-07-25 04:45:05 +0000536 ldapConfigObjectName, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530537}
538
Ed Tanous6c51eab2021-06-03 12:30:29 -0700539/**
540 * @brief parses the authentication section under the LDAP
541 * @param input JSON data
542 * @param asyncResp pointer to the JSON response
543 * @param userName userName to be filled from the given JSON.
544 * @param password password to be filled from the given JSON.
545 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700546inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700547 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
548 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700550 std::optional<std::string> authType;
551
552 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
553 authType, "Username", username, "Password",
554 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700555 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700556 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700557 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700558 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530559 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700560 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530561 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700562 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530563 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700564 messages::propertyValueNotInList(asyncResp->res, *authType,
565 "AuthenticationType");
566 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530567 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700568}
569/**
570 * @brief parses the LDAPService section under the LDAP
571 * @param input JSON data
572 * @param asyncResp pointer to the JSON response
573 * @param baseDNList baseDN to be filled from the given JSON.
574 * @param userNameAttribute userName to be filled from the given JSON.
575 * @param groupaAttribute password to be filled from the given JSON.
576 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530577
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700578inline void
579 parseLDAPServiceJson(nlohmann::json input,
580 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
581 std::optional<std::vector<std::string>>& baseDNList,
582 std::optional<std::string>& userNameAttribute,
583 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700584{
585 std::optional<nlohmann::json> searchSettings;
586
587 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
588 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530589 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700590 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530591 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700592 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530593 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700594 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530595 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700596 if (!json_util::readJson(*searchSettings, asyncResp->res,
597 "BaseDistinguishedNames", baseDNList,
598 "UsernameAttribute", userNameAttribute,
599 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530600 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700601 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530602 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700603}
604/**
605 * @brief updates the LDAP server address and updates the
606 json response with the new value.
607 * @param serviceAddressList address to be updated.
608 * @param asyncResp pointer to the JSON response
609 * @param ldapServerElementName Type of LDAP
610 server(openLDAP/ActiveDirectory)
611 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530612
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700613inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700614 const std::vector<std::string>& serviceAddressList,
615 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
616 const std::string& ldapServerElementName,
617 const std::string& ldapConfigObject)
618{
619 crow::connections::systemBus->async_method_call(
620 [asyncResp, ldapServerElementName,
621 serviceAddressList](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700622 if (ec)
623 {
624 BMCWEB_LOG_DEBUG
625 << "Error Occurred in updating the service address";
626 messages::internalError(asyncResp->res);
627 return;
628 }
629 std::vector<std::string> modifiedserviceAddressList = {
630 serviceAddressList.front()};
631 asyncResp->res.jsonValue[ldapServerElementName]["ServiceAddresses"] =
632 modifiedserviceAddressList;
633 if ((serviceAddressList).size() > 1)
634 {
635 messages::propertyValueModified(asyncResp->res, "ServiceAddresses",
636 serviceAddressList.front());
637 }
638 BMCWEB_LOG_DEBUG << "Updated the service address";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700639 },
640 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
641 ldapConfigInterface, "LDAPServerURI",
Ed Tanous168e20c2021-12-13 14:39:53 -0800642 dbus::utility::DbusVariantType(serviceAddressList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700643}
644/**
645 * @brief updates the LDAP Bind DN and updates the
646 json response with the new value.
647 * @param username name of the user which needs to be updated.
648 * @param asyncResp pointer to the JSON response
649 * @param ldapServerElementName Type of LDAP
650 server(openLDAP/ActiveDirectory)
651 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530652
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700653inline void
654 handleUserNamePatch(const std::string& username,
655 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
656 const std::string& ldapServerElementName,
657 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700658{
659 crow::connections::systemBus->async_method_call(
660 [asyncResp, username,
661 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700662 if (ec)
663 {
664 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
665 messages::internalError(asyncResp->res);
666 return;
667 }
668 asyncResp->res
669 .jsonValue[ldapServerElementName]["Authentication"]["Username"] =
670 username;
671 BMCWEB_LOG_DEBUG << "Updated the username";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700672 },
673 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800674 ldapConfigInterface, "LDAPBindDN",
675 dbus::utility::DbusVariantType(username));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700676}
677
678/**
679 * @brief updates the LDAP password
680 * @param password : ldap password which needs to be updated.
681 * @param asyncResp pointer to the JSON response
682 * @param ldapServerElementName Type of LDAP
683 * server(openLDAP/ActiveDirectory)
684 */
685
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700686inline void
687 handlePasswordPatch(const std::string& password,
688 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
689 const std::string& ldapServerElementName,
690 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700691{
692 crow::connections::systemBus->async_method_call(
693 [asyncResp, password,
694 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700695 if (ec)
696 {
697 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
698 messages::internalError(asyncResp->res);
699 return;
700 }
701 asyncResp->res
702 .jsonValue[ldapServerElementName]["Authentication"]["Password"] =
703 "";
704 BMCWEB_LOG_DEBUG << "Updated the password";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700705 },
706 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
707 ldapConfigInterface, "LDAPBindDNPassword",
Ed Tanous168e20c2021-12-13 14:39:53 -0800708 dbus::utility::DbusVariantType(password));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700709}
710
711/**
712 * @brief updates the LDAP BaseDN and updates the
713 json response with the new value.
714 * @param baseDNList baseDN list which needs to be updated.
715 * @param asyncResp pointer to the JSON response
716 * @param ldapServerElementName Type of LDAP
717 server(openLDAP/ActiveDirectory)
718 */
719
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700720inline void
721 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
722 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
723 const std::string& ldapServerElementName,
724 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700725{
726 crow::connections::systemBus->async_method_call(
727 [asyncResp, baseDNList,
728 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700729 if (ec)
730 {
731 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
732 messages::internalError(asyncResp->res);
733 return;
734 }
735 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
736 auto& searchSettingsJson =
737 serverTypeJson["LDAPService"]["SearchSettings"];
738 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
739 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
740 if (baseDNList.size() > 1)
741 {
742 messages::propertyValueModified(
743 asyncResp->res, "BaseDistinguishedNames", baseDNList.front());
744 }
745 BMCWEB_LOG_DEBUG << "Updated the base DN";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700746 },
747 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
748 ldapConfigInterface, "LDAPBaseDN",
Ed Tanous168e20c2021-12-13 14:39:53 -0800749 dbus::utility::DbusVariantType(baseDNList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700750}
751/**
752 * @brief updates the LDAP user name attribute and updates the
753 json response with the new value.
754 * @param userNameAttribute attribute to be updated.
755 * @param asyncResp pointer to the JSON response
756 * @param ldapServerElementName Type of LDAP
757 server(openLDAP/ActiveDirectory)
758 */
759
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700760inline void
761 handleUserNameAttrPatch(const std::string& userNameAttribute,
762 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
763 const std::string& ldapServerElementName,
764 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700765{
766 crow::connections::systemBus->async_method_call(
767 [asyncResp, userNameAttribute,
768 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700769 if (ec)
770 {
771 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
772 "username attribute";
773 messages::internalError(asyncResp->res);
774 return;
775 }
776 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
777 auto& searchSettingsJson =
778 serverTypeJson["LDAPService"]["SearchSettings"];
779 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
780 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700781 },
782 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
783 ldapConfigInterface, "UserNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800784 dbus::utility::DbusVariantType(userNameAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700785}
786/**
787 * @brief updates the LDAP group attribute and updates the
788 json response with the new value.
789 * @param groupsAttribute attribute to be updated.
790 * @param asyncResp pointer to the JSON response
791 * @param ldapServerElementName Type of LDAP
792 server(openLDAP/ActiveDirectory)
793 */
794
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700795inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700796 const std::string& groupsAttribute,
797 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
798 const std::string& ldapServerElementName,
799 const std::string& ldapConfigObject)
800{
801 crow::connections::systemBus->async_method_call(
802 [asyncResp, groupsAttribute,
803 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700804 if (ec)
805 {
806 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
807 "groupname attribute";
808 messages::internalError(asyncResp->res);
809 return;
810 }
811 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
812 auto& searchSettingsJson =
813 serverTypeJson["LDAPService"]["SearchSettings"];
814 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
815 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700816 },
817 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
818 ldapConfigInterface, "GroupNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800819 dbus::utility::DbusVariantType(groupsAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700820}
821/**
822 * @brief updates the LDAP service enable and updates the
823 json response with the new value.
824 * @param input JSON data.
825 * @param asyncResp pointer to the JSON response
826 * @param ldapServerElementName Type of LDAP
827 server(openLDAP/ActiveDirectory)
828 */
829
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700830inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700831 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
832 const std::string& ldapServerElementName,
833 const std::string& ldapConfigObject)
834{
835 crow::connections::systemBus->async_method_call(
836 [asyncResp, serviceEnabled,
837 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700838 if (ec)
839 {
840 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the service enable";
841 messages::internalError(asyncResp->res);
842 return;
843 }
844 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
845 serviceEnabled;
846 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700847 },
848 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800849 ldapEnableInterface, "Enabled",
850 dbus::utility::DbusVariantType(serviceEnabled));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700851}
852
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700853inline void
854 handleAuthMethodsPatch(nlohmann::json& input,
855 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700856{
857 std::optional<bool> basicAuth;
858 std::optional<bool> cookie;
859 std::optional<bool> sessionToken;
860 std::optional<bool> xToken;
861 std::optional<bool> tls;
862
863 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
864 "Cookie", cookie, "SessionToken", sessionToken,
865 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530866 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700867 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
868 return;
869 }
870
871 // Make a copy of methods configuration
872 persistent_data::AuthConfigMethods authMethodsConfig =
873 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
874
875 if (basicAuth)
876 {
877#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
878 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800879 asyncResp->res,
880 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700881 return;
882#endif
883 authMethodsConfig.basic = *basicAuth;
884 }
885
886 if (cookie)
887 {
888#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800889 messages::actionNotSupported(
890 asyncResp->res,
891 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700892 return;
893#endif
894 authMethodsConfig.cookie = *cookie;
895 }
896
897 if (sessionToken)
898 {
899#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
900 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800901 asyncResp->res,
902 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700903 return;
904#endif
905 authMethodsConfig.sessionToken = *sessionToken;
906 }
907
908 if (xToken)
909 {
910#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800911 messages::actionNotSupported(
912 asyncResp->res,
913 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700914 return;
915#endif
916 authMethodsConfig.xtoken = *xToken;
917 }
918
919 if (tls)
920 {
921#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800922 messages::actionNotSupported(
923 asyncResp->res,
924 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700925 return;
926#endif
927 authMethodsConfig.tls = *tls;
928 }
929
930 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
931 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
932 !authMethodsConfig.tls)
933 {
934 // Do not allow user to disable everything
935 messages::actionNotSupported(asyncResp->res,
936 "of disabling all available methods");
937 return;
938 }
939
940 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
941 authMethodsConfig);
942 // Save configuration immediately
943 persistent_data::getConfig().writeData();
944
945 messages::success(asyncResp->res);
946}
947
948/**
949 * @brief Get the required values from the given JSON, validates the
950 * value and create the LDAP config object.
951 * @param input JSON data
952 * @param asyncResp pointer to the JSON response
953 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
954 */
955
956inline void handleLDAPPatch(nlohmann::json& input,
957 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
958 const std::string& serverType)
959{
960 std::string dbusObjectPath;
961 if (serverType == "ActiveDirectory")
962 {
963 dbusObjectPath = adConfigObject;
964 }
965 else if (serverType == "LDAP")
966 {
967 dbusObjectPath = ldapConfigObjectName;
968 }
969 else
970 {
971 return;
972 }
973
974 std::optional<nlohmann::json> authentication;
975 std::optional<nlohmann::json> ldapService;
976 std::optional<std::vector<std::string>> serviceAddressList;
977 std::optional<bool> serviceEnabled;
978 std::optional<std::vector<std::string>> baseDNList;
979 std::optional<std::string> userNameAttribute;
980 std::optional<std::string> groupsAttribute;
981 std::optional<std::string> userName;
982 std::optional<std::string> password;
983 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
984
985 if (!json_util::readJson(input, asyncResp->res, "Authentication",
986 authentication, "LDAPService", ldapService,
987 "ServiceAddresses", serviceAddressList,
988 "ServiceEnabled", serviceEnabled,
989 "RemoteRoleMapping", remoteRoleMapData))
990 {
991 return;
992 }
993
994 if (authentication)
995 {
996 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
997 password);
998 }
999 if (ldapService)
1000 {
1001 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1002 userNameAttribute, groupsAttribute);
1003 }
1004 if (serviceAddressList)
1005 {
Ed Tanous26f69762022-01-25 09:49:11 -08001006 if (serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301007 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001008 messages::propertyValueNotInList(asyncResp->res, "[]",
1009 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001010 return;
1011 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001012 }
1013 if (baseDNList)
1014 {
Ed Tanous26f69762022-01-25 09:49:11 -08001015 if (baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +05301016 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001017 messages::propertyValueNotInList(asyncResp->res, "[]",
1018 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301019 return;
1020 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001021 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301022
Ed Tanous6c51eab2021-06-03 12:30:29 -07001023 // nothing to update, then return
1024 if (!userName && !password && !serviceAddressList && !baseDNList &&
1025 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1026 !remoteRoleMapData)
1027 {
1028 return;
1029 }
1030
1031 // Get the existing resource first then keep modifying
1032 // whenever any property gets updated.
Ed Tanous002d39b2022-05-31 08:59:27 -07001033 getLDAPConfigData(
1034 serverType,
1035 [asyncResp, userName, password, baseDNList, userNameAttribute,
1036 groupsAttribute, serviceAddressList, serviceEnabled, dbusObjectPath,
1037 remoteRoleMapData](bool success, const LDAPConfigData& confData,
1038 const std::string& serverT) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001039 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301040 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001041 messages::internalError(asyncResp->res);
1042 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301043 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001044 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1045 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301046 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001047 // Disable the service first and update the rest of
1048 // the properties.
1049 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301050 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001051
Ratan Gupta8a07d282019-03-16 08:33:47 +05301052 if (serviceAddressList)
1053 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001054 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1055 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301056 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001057 if (userName)
1058 {
1059 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1060 }
1061 if (password)
1062 {
1063 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1064 }
1065
Ratan Gupta8a07d282019-03-16 08:33:47 +05301066 if (baseDNList)
1067 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001068 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1069 }
1070 if (userNameAttribute)
1071 {
1072 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1073 dbusObjectPath);
1074 }
1075 if (groupsAttribute)
1076 {
1077 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1078 dbusObjectPath);
1079 }
1080 if (serviceEnabled)
1081 {
1082 // if user has given the value as true then enable
1083 // the service. if user has given false then no-op
1084 // as service is already stopped.
1085 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301086 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001087 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1088 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301089 }
1090 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001091 else
1092 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001093 // if user has not given the service enabled value
1094 // then revert it to the same state as it was
1095 // before.
1096 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1097 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001098 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001099
Ed Tanous6c51eab2021-06-03 12:30:29 -07001100 if (remoteRoleMapData)
1101 {
1102 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1103 *remoteRoleMapData);
1104 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001105 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001106}
1107
1108inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1109 const std::string& username,
1110 std::optional<std::string> password,
1111 std::optional<bool> enabled,
1112 std::optional<std::string> roleId,
1113 std::optional<bool> locked)
1114{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301115 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1116 tempObjPath /= username;
1117 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001118
1119 dbus::utility::checkDbusPathExists(
1120 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001121 [dbusObjectPath, username, password(std::move(password)),
1122 roleId(std::move(roleId)), enabled, locked,
1123 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001124 if (rc <= 0)
1125 {
1126 messages::resourceNotFound(asyncResp->res,
1127 "#ManagerAccount.v1_4_0.ManagerAccount",
1128 username);
1129 return;
1130 }
1131
1132 if (password)
1133 {
1134 int retval = pamUpdatePassword(username, *password);
1135
1136 if (retval == PAM_USER_UNKNOWN)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001137 {
1138 messages::resourceNotFound(
1139 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1140 username);
Ed Tanous002d39b2022-05-31 08:59:27 -07001141 }
1142 else if (retval == PAM_AUTHTOK_ERR)
1143 {
1144 // If password is invalid
1145 messages::propertyValueFormatError(asyncResp->res, *password,
1146 "Password");
1147 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1148 }
1149 else if (retval != PAM_SUCCESS)
1150 {
1151 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001152 return;
1153 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001154 else
Ed Tanous6c51eab2021-06-03 12:30:29 -07001155 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001156 messages::success(asyncResp->res);
1157 }
1158 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001159
Ed Tanous002d39b2022-05-31 08:59:27 -07001160 if (enabled)
1161 {
1162 crow::connections::systemBus->async_method_call(
1163 [asyncResp](const boost::system::error_code ec) {
1164 if (ec)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001165 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001166 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001167 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001168 return;
1169 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001170 messages::success(asyncResp->res);
1171 return;
1172 },
1173 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1174 "org.freedesktop.DBus.Properties", "Set",
1175 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1176 dbus::utility::DbusVariantType{*enabled});
1177 }
1178
1179 if (roleId)
1180 {
1181 std::string priv = getPrivilegeFromRoleId(*roleId);
1182 if (priv.empty())
1183 {
1184 messages::propertyValueNotInList(asyncResp->res, *roleId,
1185 "RoleId");
1186 return;
1187 }
1188 if (priv == "priv-noaccess")
1189 {
1190 priv = "";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001191 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001192
Ed Tanous002d39b2022-05-31 08:59:27 -07001193 crow::connections::systemBus->async_method_call(
1194 [asyncResp](const boost::system::error_code ec) {
1195 if (ec)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001196 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001197 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1198 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001199 return;
1200 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001201 messages::success(asyncResp->res);
1202 },
1203 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1204 "org.freedesktop.DBus.Properties", "Set",
1205 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1206 dbus::utility::DbusVariantType{priv});
1207 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001208
Ed Tanous002d39b2022-05-31 08:59:27 -07001209 if (locked)
1210 {
1211 // admin can unlock the account which is locked by
1212 // successive authentication failures but admin should
1213 // not be allowed to lock an account.
1214 if (*locked)
1215 {
1216 messages::propertyValueNotInList(asyncResp->res, "true",
1217 "Locked");
1218 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001219 }
1220
Ed Tanous002d39b2022-05-31 08:59:27 -07001221 crow::connections::systemBus->async_method_call(
1222 [asyncResp](const boost::system::error_code ec) {
1223 if (ec)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001224 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001225 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1226 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001227 return;
1228 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001229 messages::success(asyncResp->res);
1230 return;
1231 },
1232 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1233 "org.freedesktop.DBus.Properties", "Set",
1234 "xyz.openbmc_project.User.Attributes",
1235 "UserLockedForFailedAttempt",
1236 dbus::utility::DbusVariantType{*locked});
1237 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001238 });
1239}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001240
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001241inline void handleAccountServiceHead(
1242 App& app, const crow::Request& req,
1243 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001244{
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001245
Ed Tanous1ef4c342022-05-12 16:12:36 -07001246 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1247 {
1248 return;
1249 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001250 asyncResp->res.addHeader(
1251 boost::beast::http::field::link,
1252 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1253}
1254
1255inline void
1256 handleAccountServiceGet(App& app, const crow::Request& req,
1257 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1258{
1259 handleAccountServiceHead(app, req, asyncResp);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001260 const persistent_data::AuthConfigMethods& authMethodsConfig =
1261 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1262
1263 nlohmann::json& json = asyncResp->res.jsonValue;
1264 json["@odata.id"] = "/redfish/v1/AccountService";
1265 json["@odata.type"] = "#AccountService."
1266 "v1_10_0.AccountService";
1267 json["Id"] = "AccountService";
1268 json["Name"] = "Account Service";
1269 json["Description"] = "Account Service";
1270 json["ServiceEnabled"] = true;
1271 json["MaxPasswordLength"] = 20;
1272 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1273 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
1274 json["Oem"]["OpenBMC"]["@odata.type"] =
1275 "#OemAccountService.v1_0_0.AccountService";
1276 json["Oem"]["OpenBMC"]["@odata.id"] =
1277 "/redfish/v1/AccountService#/Oem/OpenBMC";
1278 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1279 authMethodsConfig.basic;
1280 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1281 authMethodsConfig.sessionToken;
1282 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1283 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1284 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1285
1286 // /redfish/v1/AccountService/LDAP/Certificates is something only
1287 // ConfigureManager can access then only display when the user has
1288 // permissions ConfigureManager
1289 Privileges effectiveUserPrivileges =
1290 redfish::getUserPrivileges(req.userRole);
1291
1292 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1293 effectiveUserPrivileges))
1294 {
1295 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1296 "/redfish/v1/AccountService/LDAP/Certificates";
1297 }
1298 crow::connections::systemBus->async_method_call(
1299 [asyncResp](const boost::system::error_code ec,
1300 const dbus::utility::DBusPropertiesMap& propertiesList) {
1301 if (ec)
1302 {
1303 messages::internalError(asyncResp->res);
1304 return;
1305 }
1306 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1307 << "properties for AccountService";
1308 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
1309 property : propertiesList)
1310 {
1311 if (property.first == "MinPasswordLength")
1312 {
1313 const uint8_t* value = std::get_if<uint8_t>(&property.second);
1314 if (value != nullptr)
1315 {
1316 asyncResp->res.jsonValue["MinPasswordLength"] = *value;
1317 }
1318 }
1319 if (property.first == "AccountUnlockTimeout")
1320 {
1321 const uint32_t* value = std::get_if<uint32_t>(&property.second);
1322 if (value != nullptr)
1323 {
1324 asyncResp->res.jsonValue["AccountLockoutDuration"] = *value;
1325 }
1326 }
1327 if (property.first == "MaxLoginAttemptBeforeLockout")
1328 {
1329 const uint16_t* value = std::get_if<uint16_t>(&property.second);
1330 if (value != nullptr)
1331 {
1332 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1333 *value;
1334 }
1335 }
1336 }
1337 },
1338 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1339 "org.freedesktop.DBus.Properties", "GetAll",
1340 "xyz.openbmc_project.User.AccountPolicy");
1341
Ed Tanous02cad962022-06-30 16:50:15 -07001342 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001343 const std::string& ldapType) {
1344 if (!success)
1345 {
1346 return;
1347 }
1348 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1349 };
1350
1351 getLDAPConfigData("LDAP", callback);
1352 getLDAPConfigData("ActiveDirectory", callback);
1353}
1354
1355inline void handleAccountServicePatch(
1356 App& app, const crow::Request& req,
1357 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1358{
1359 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1360 {
1361 return;
1362 }
1363 std::optional<uint32_t> unlockTimeout;
1364 std::optional<uint16_t> lockoutThreshold;
1365 std::optional<uint8_t> minPasswordLength;
1366 std::optional<uint16_t> maxPasswordLength;
1367 std::optional<nlohmann::json> ldapObject;
1368 std::optional<nlohmann::json> activeDirectoryObject;
1369 std::optional<nlohmann::json> oemObject;
1370
1371 if (!json_util::readJsonPatch(
1372 req, asyncResp->res, "AccountLockoutDuration", unlockTimeout,
1373 "AccountLockoutThreshold", lockoutThreshold, "MaxPasswordLength",
1374 maxPasswordLength, "MinPasswordLength", minPasswordLength, "LDAP",
1375 ldapObject, "ActiveDirectory", activeDirectoryObject, "Oem",
1376 oemObject))
1377 {
1378 return;
1379 }
1380
1381 if (minPasswordLength)
1382 {
1383 crow::connections::systemBus->async_method_call(
1384 [asyncResp](const boost::system::error_code ec) {
1385 if (ec)
1386 {
1387 messages::internalError(asyncResp->res);
1388 return;
1389 }
1390 messages::success(asyncResp->res);
1391 },
1392 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1393 "org.freedesktop.DBus.Properties", "Set",
1394 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
1395 dbus::utility::DbusVariantType(*minPasswordLength));
1396 }
1397
1398 if (maxPasswordLength)
1399 {
1400 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1401 }
1402
1403 if (ldapObject)
1404 {
1405 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1406 }
1407
1408 if (std::optional<nlohmann::json> oemOpenBMCObject;
1409 oemObject && json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1410 oemOpenBMCObject))
1411 {
1412 if (std::optional<nlohmann::json> authMethodsObject;
1413 oemOpenBMCObject &&
1414 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1415 "AuthMethods", authMethodsObject))
1416 {
1417 if (authMethodsObject)
1418 {
1419 handleAuthMethodsPatch(*authMethodsObject, asyncResp);
1420 }
1421 }
1422 }
1423
1424 if (activeDirectoryObject)
1425 {
1426 handleLDAPPatch(*activeDirectoryObject, asyncResp, "ActiveDirectory");
1427 }
1428
1429 if (unlockTimeout)
1430 {
1431 crow::connections::systemBus->async_method_call(
1432 [asyncResp](const boost::system::error_code ec) {
1433 if (ec)
1434 {
1435 messages::internalError(asyncResp->res);
1436 return;
1437 }
1438 messages::success(asyncResp->res);
1439 },
1440 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1441 "org.freedesktop.DBus.Properties", "Set",
1442 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
1443 dbus::utility::DbusVariantType(*unlockTimeout));
1444 }
1445 if (lockoutThreshold)
1446 {
1447 crow::connections::systemBus->async_method_call(
1448 [asyncResp](const boost::system::error_code ec) {
1449 if (ec)
1450 {
1451 messages::internalError(asyncResp->res);
1452 return;
1453 }
1454 messages::success(asyncResp->res);
1455 },
1456 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1457 "org.freedesktop.DBus.Properties", "Set",
1458 "xyz.openbmc_project.User.AccountPolicy",
1459 "MaxLoginAttemptBeforeLockout",
1460 dbus::utility::DbusVariantType(*lockoutThreshold));
1461 }
1462}
1463
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001464inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001465 App& app, const crow::Request& req,
1466 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1467{
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001468
Ed Tanous1ef4c342022-05-12 16:12:36 -07001469 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1470 {
1471 return;
1472 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001473 asyncResp->res.addHeader(
1474 boost::beast::http::field::link,
1475 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1476}
1477
1478inline void handleAccountCollectionGet(
1479 App& app, const crow::Request& req,
1480 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1481{
1482 handleAccountCollectionHead(app, req, asyncResp);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001483
1484 asyncResp->res.jsonValue["@odata.id"] =
1485 "/redfish/v1/AccountService/Accounts";
1486 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1487 "ManagerAccountCollection";
1488 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1489 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1490
1491 Privileges effectiveUserPrivileges =
1492 redfish::getUserPrivileges(req.userRole);
1493
1494 std::string thisUser;
1495 if (req.session)
1496 {
1497 thisUser = req.session->username;
1498 }
1499 crow::connections::systemBus->async_method_call(
1500 [asyncResp, thisUser, effectiveUserPrivileges](
1501 const boost::system::error_code ec,
1502 const dbus::utility::ManagedObjectType& users) {
1503 if (ec)
1504 {
1505 messages::internalError(asyncResp->res);
1506 return;
1507 }
1508
1509 bool userCanSeeAllAccounts =
1510 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1511
1512 bool userCanSeeSelf =
1513 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1514
1515 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1516 memberArray = nlohmann::json::array();
1517
1518 for (const auto& userpath : users)
1519 {
1520 std::string user = userpath.first.filename();
1521 if (user.empty())
1522 {
1523 messages::internalError(asyncResp->res);
1524 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1525
1526 return;
1527 }
1528
1529 // As clarified by Redfish here:
1530 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1531 // Users without ConfigureUsers, only see their own
1532 // account. Users with ConfigureUsers, see all
1533 // accounts.
1534 if (userCanSeeAllAccounts || (thisUser == user && userCanSeeSelf))
1535 {
1536 nlohmann::json::object_t member;
1537 member["@odata.id"] =
1538 "/redfish/v1/AccountService/Accounts/" + user;
1539 memberArray.push_back(std::move(member));
1540 }
1541 }
1542 asyncResp->res.jsonValue["Members@odata.count"] = memberArray.size();
1543 },
1544 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1545 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1546}
1547
1548inline void handleAccountCollectionPost(
1549 App& app, const crow::Request& req,
1550 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1551{
1552 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1553 {
1554 return;
1555 }
1556 std::string username;
1557 std::string password;
1558 std::optional<std::string> roleId("User");
1559 std::optional<bool> enabled = true;
1560 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", username,
1561 "Password", password, "RoleId", roleId,
1562 "Enabled", enabled))
1563 {
1564 return;
1565 }
1566
1567 std::string priv = getPrivilegeFromRoleId(*roleId);
1568 if (priv.empty())
1569 {
1570 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
1571 return;
1572 }
1573 // TODO: Following override will be reverted once support in
1574 // phosphor-user-manager is added. In order to avoid dependency
1575 // issues, this is added in bmcweb, which will removed, once
1576 // phosphor-user-manager supports priv-noaccess.
1577 if (priv == "priv-noaccess")
1578 {
1579 roleId = "";
1580 }
1581 else
1582 {
1583 roleId = priv;
1584 }
1585
1586 // Reading AllGroups property
1587 sdbusplus::asio::getProperty<std::vector<std::string>>(
1588 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1589 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager",
1590 "AllGroups",
1591 [asyncResp, username, password{std::move(password)}, roleId,
1592 enabled](const boost::system::error_code ec,
1593 const std::vector<std::string>& allGroupsList) {
1594 if (ec)
1595 {
1596 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1597 messages::internalError(asyncResp->res);
1598 return;
1599 }
1600
1601 if (allGroupsList.empty())
1602 {
1603 messages::internalError(asyncResp->res);
1604 return;
1605 }
1606
1607 crow::connections::systemBus->async_method_call(
1608 [asyncResp, username, password](const boost::system::error_code ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -05001609 sdbusplus::message_t& m) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001610 if (ec2)
1611 {
1612 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1613 return;
1614 }
1615
1616 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1617 {
1618 // At this point we have a user that's been
1619 // created, but the password set
1620 // failed.Something is wrong, so delete the user
1621 // that we've already created
1622 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1623 tempObjPath /= username;
1624 const std::string userPath(tempObjPath);
1625
1626 crow::connections::systemBus->async_method_call(
1627 [asyncResp, password](const boost::system::error_code ec3) {
1628 if (ec3)
1629 {
1630 messages::internalError(asyncResp->res);
1631 return;
1632 }
1633
1634 // If password is invalid
1635 messages::propertyValueFormatError(asyncResp->res, password,
1636 "Password");
1637 },
1638 "xyz.openbmc_project.User.Manager", userPath,
1639 "xyz.openbmc_project.Object.Delete", "Delete");
1640
1641 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1642 return;
1643 }
1644
1645 messages::created(asyncResp->res);
1646 asyncResp->res.addHeader(
1647 "Location", "/redfish/v1/AccountService/Accounts/" + username);
1648 },
1649 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1650 "xyz.openbmc_project.User.Manager", "CreateUser", username,
1651 allGroupsList, *roleId, *enabled);
1652 });
1653}
1654
1655inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001656 handleAccountHead(App& app, const crow::Request& req,
1657 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1658 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001659{
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001660
Ed Tanous1ef4c342022-05-12 16:12:36 -07001661 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1662 {
1663 return;
1664 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001665 asyncResp->res.addHeader(
1666 boost::beast::http::field::link,
1667 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1668}
1669inline void
1670 handleAccountGet(App& app, const crow::Request& req,
1671 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1672 const std::string& accountName)
1673{
1674 handleAccountHead(app, req, asyncResp, accountName);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001675#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1676 // If authentication is disabled, there are no user accounts
1677 messages::resourceNotFound(
1678 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", accountName);
1679 return;
1680
1681#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1682 if (req.session == nullptr)
1683 {
1684 messages::internalError(asyncResp->res);
1685 return;
1686 }
1687 if (req.session->username != accountName)
1688 {
1689 // At this point we've determined that the user is trying to
1690 // modify a user that isn't them. We need to verify that they
1691 // have permissions to modify other users, so re-run the auth
1692 // check with the same permissions, minus ConfigureSelf.
1693 Privileges effectiveUserPrivileges =
1694 redfish::getUserPrivileges(req.userRole);
1695 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
1696 "ConfigureManager"};
1697 if (!effectiveUserPrivileges.isSupersetOf(
1698 requiredPermissionsToChangeNonSelf))
1699 {
1700 BMCWEB_LOG_DEBUG << "GET Account denied access";
1701 messages::insufficientPrivilege(asyncResp->res);
1702 return;
1703 }
1704 }
1705
1706 crow::connections::systemBus->async_method_call(
1707 [asyncResp,
1708 accountName](const boost::system::error_code ec,
1709 const dbus::utility::ManagedObjectType& users) {
1710 if (ec)
1711 {
1712 messages::internalError(asyncResp->res);
1713 return;
1714 }
1715 const auto userIt = std::find_if(
1716 users.begin(), users.end(),
1717 [accountName](
1718 const std::pair<sdbusplus::message::object_path,
1719 dbus::utility::DBusInteracesMap>& user) {
1720 return accountName == user.first.filename();
1721 });
1722
1723 if (userIt == users.end())
1724 {
1725 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1726 accountName);
1727 return;
1728 }
1729
1730 asyncResp->res.jsonValue["@odata.type"] =
1731 "#ManagerAccount.v1_4_0.ManagerAccount";
1732 asyncResp->res.jsonValue["Name"] = "User Account";
1733 asyncResp->res.jsonValue["Description"] = "User Account";
1734 asyncResp->res.jsonValue["Password"] = nullptr;
1735 asyncResp->res.jsonValue["AccountTypes"] = {"Redfish"};
1736
1737 for (const auto& interface : userIt->second)
1738 {
1739 if (interface.first == "xyz.openbmc_project.User.Attributes")
1740 {
1741 for (const auto& property : interface.second)
1742 {
1743 if (property.first == "UserEnabled")
1744 {
1745 const bool* userEnabled =
1746 std::get_if<bool>(&property.second);
1747 if (userEnabled == nullptr)
1748 {
1749 BMCWEB_LOG_ERROR << "UserEnabled wasn't a bool";
1750 messages::internalError(asyncResp->res);
1751 return;
1752 }
1753 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
1754 }
1755 else if (property.first == "UserLockedForFailedAttempt")
1756 {
1757 const bool* userLocked =
1758 std::get_if<bool>(&property.second);
1759 if (userLocked == nullptr)
1760 {
1761 BMCWEB_LOG_ERROR << "UserLockedForF"
1762 "ailedAttempt "
1763 "wasn't a bool";
1764 messages::internalError(asyncResp->res);
1765 return;
1766 }
1767 asyncResp->res.jsonValue["Locked"] = *userLocked;
1768 asyncResp->res
1769 .jsonValue["Locked@Redfish.AllowableValues"] = {
1770 "false"}; // can only unlock accounts
1771 }
1772 else if (property.first == "UserPrivilege")
1773 {
1774 const std::string* userPrivPtr =
1775 std::get_if<std::string>(&property.second);
1776 if (userPrivPtr == nullptr)
1777 {
1778 BMCWEB_LOG_ERROR << "UserPrivilege wasn't a "
1779 "string";
1780 messages::internalError(asyncResp->res);
1781 return;
1782 }
1783 std::string role = getRoleIdFromPrivilege(*userPrivPtr);
1784 if (role.empty())
1785 {
1786 BMCWEB_LOG_ERROR << "Invalid user role";
1787 messages::internalError(asyncResp->res);
1788 return;
1789 }
1790 asyncResp->res.jsonValue["RoleId"] = role;
1791
1792 nlohmann::json& roleEntry =
1793 asyncResp->res.jsonValue["Links"]["Role"];
1794 roleEntry["@odata.id"] =
1795 "/redfish/v1/AccountService/Roles/" + role;
1796 }
1797 else if (property.first == "UserPasswordExpired")
1798 {
1799 const bool* userPasswordExpired =
1800 std::get_if<bool>(&property.second);
1801 if (userPasswordExpired == nullptr)
1802 {
1803 BMCWEB_LOG_ERROR
1804 << "UserPasswordExpired wasn't a bool";
1805 messages::internalError(asyncResp->res);
1806 return;
1807 }
1808 asyncResp->res.jsonValue["PasswordChangeRequired"] =
1809 *userPasswordExpired;
1810 }
1811 }
1812 }
1813 }
1814
1815 asyncResp->res.jsonValue["@odata.id"] =
1816 "/redfish/v1/AccountService/Accounts/" + accountName;
1817 asyncResp->res.jsonValue["Id"] = accountName;
1818 asyncResp->res.jsonValue["UserName"] = accountName;
1819 },
1820 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1821 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1822}
1823
1824inline void
1825 handleAccounttDelete(App& app, const crow::Request& req,
1826 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1827 const std::string& username)
1828{
1829 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1830 {
1831 return;
1832 }
1833
1834#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1835 // If authentication is disabled, there are no user accounts
1836 messages::resourceNotFound(
1837 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
1838 return;
1839
1840#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1841 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1842 tempObjPath /= username;
1843 const std::string userPath(tempObjPath);
1844
1845 crow::connections::systemBus->async_method_call(
1846 [asyncResp, username](const boost::system::error_code ec) {
1847 if (ec)
1848 {
1849 messages::resourceNotFound(asyncResp->res,
1850 "#ManagerAccount.v1_4_0.ManagerAccount",
1851 username);
1852 return;
1853 }
1854
1855 messages::accountRemoved(asyncResp->res);
1856 },
1857 "xyz.openbmc_project.User.Manager", userPath,
1858 "xyz.openbmc_project.Object.Delete", "Delete");
1859}
1860
1861inline void
1862 handleAccountPatch(App& app, const crow::Request& req,
1863 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1864 const std::string& username)
1865{
1866 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1867 {
1868 return;
1869 }
1870#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1871 // If authentication is disabled, there are no user accounts
1872 messages::resourceNotFound(
1873 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
1874 return;
1875
1876#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1877 std::optional<std::string> newUserName;
1878 std::optional<std::string> password;
1879 std::optional<bool> enabled;
1880 std::optional<std::string> roleId;
1881 std::optional<bool> locked;
1882
1883 if (req.session == nullptr)
1884 {
1885 messages::internalError(asyncResp->res);
1886 return;
1887 }
1888
1889 Privileges effectiveUserPrivileges =
1890 redfish::getUserPrivileges(req.userRole);
1891 Privileges configureUsers = {"ConfigureUsers"};
1892 bool userHasConfigureUsers =
1893 effectiveUserPrivileges.isSupersetOf(configureUsers);
1894 if (userHasConfigureUsers)
1895 {
1896 // Users with ConfigureUsers can modify for all users
1897 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
1898 newUserName, "Password", password,
1899 "RoleId", roleId, "Enabled", enabled,
1900 "Locked", locked))
1901 {
1902 return;
1903 }
1904 }
1905 else
1906 {
1907 // ConfigureSelf accounts can only modify their own account
1908 if (username != req.session->username)
1909 {
1910 messages::insufficientPrivilege(asyncResp->res);
1911 return;
1912 }
1913
1914 // ConfigureSelf accounts can only modify their password
1915 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
1916 password))
1917 {
1918 return;
1919 }
1920 }
1921
1922 // if user name is not provided in the patch method or if it
1923 // matches the user name in the URI, then we are treating it as
1924 // updating user properties other then username. If username
1925 // provided doesn't match the URI, then we are treating this as
1926 // user rename request.
1927 if (!newUserName || (newUserName.value() == username))
1928 {
1929 updateUserProperties(asyncResp, username, password, enabled, roleId,
1930 locked);
1931 return;
1932 }
1933 crow::connections::systemBus->async_method_call(
1934 [asyncResp, username, password(std::move(password)),
1935 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Patrick Williams59d494e2022-07-22 19:26:55 -05001936 locked](const boost::system::error_code ec, sdbusplus::message_t& m) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001937 if (ec)
1938 {
1939 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
1940 username);
1941 return;
1942 }
1943
1944 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
1945 locked);
1946 },
1947 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1948 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1949 *newUserName);
1950}
1951
Ed Tanous6c51eab2021-06-03 12:30:29 -07001952inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001953{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001954
Ed Tanous6c51eab2021-06-03 12:30:29 -07001955 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001956 .privileges(redfish::privileges::headAccountService)
1957 .methods(boost::beast::http::verb::head)(
1958 std::bind_front(handleAccountServiceHead, std::ref(app)));
1959
1960 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001961 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07001962 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001963 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001964
Ed Tanousf5ffd802021-07-19 10:55:33 -07001965 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06001966 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07001967 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001968 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001969
Ed Tanous6c51eab2021-06-03 12:30:29 -07001970 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001971 .privileges(redfish::privileges::headManagerAccountCollection)
1972 .methods(boost::beast::http::verb::head)(
1973 std::bind_front(handleAccountCollectionHead, std::ref(app)));
1974
1975 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001976 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001977 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001978 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07001979
Ed Tanous6c51eab2021-06-03 12:30:29 -07001980 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001981 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001982 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001983 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07001984
1985 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001986 .privileges(redfish::privileges::headManagerAccount)
1987 .methods(boost::beast::http::verb::head)(
1988 std::bind_front(handleAccountHead, std::ref(app)));
1989
1990 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07001991 .privileges(redfish::privileges::getManagerAccount)
1992 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001993 std::bind_front(handleAccountGet, 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 // TODO this privilege should be using the generated endpoints, but
1997 // because of the special handling of ConfigureSelf, it's not able to
1998 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001999 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
2000 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002001 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002002
2003 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002004 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002005 .methods(boost::beast::http::verb::delete_)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002006 std::bind_front(handleAccounttDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002007}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002008
Ed Tanous1abe55e2018-09-05 08:30:59 -07002009} // namespace redfish