blob: 3d140157045f983c6e76e13c4b4adfdcb54fce89 [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 Tanous6c51eab2021-06-03 12:30:29 -07001241inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001242{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001243
Ed Tanous6c51eab2021-06-03 12:30:29 -07001244 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001245 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07001246 .methods(boost::beast::http::verb::get)(
1247 [&app](
1248 const crow::Request& req,
1249 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1250 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
Ed Tanous6c51eab2021-06-03 12:30:29 -07001251 {
1252 return;
1253 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001254 const persistent_data::AuthConfigMethods& authMethodsConfig =
1255 persistent_data::SessionStore::getInstance()
1256 .getAuthMethodsConfig();
Ed Tanous6c51eab2021-06-03 12:30:29 -07001257
Ed Tanous002d39b2022-05-31 08:59:27 -07001258 nlohmann::json& json = asyncResp->res.jsonValue;
1259 json["@odata.id"] = "/redfish/v1/AccountService";
1260 json["@odata.type"] = "#AccountService."
1261 "v1_10_0.AccountService";
1262 json["Id"] = "AccountService";
1263 json["Name"] = "Account Service";
1264 json["Description"] = "Account Service";
1265 json["ServiceEnabled"] = true;
1266 json["MaxPasswordLength"] = 20;
1267 json["Accounts"]["@odata.id"] =
1268 "/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"] =
1279 authMethodsConfig.xtoken;
1280 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] =
1281 authMethodsConfig.cookie;
1282 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] =
1283 authMethodsConfig.tls;
1284
1285 // /redfish/v1/AccountService/LDAP/Certificates is something
1286 // only ConfigureManager can access then only display when the
1287 // user has permissions ConfigureManager
1288 Privileges effectiveUserPrivileges =
1289 redfish::getUserPrivileges(req.userRole);
1290
1291 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1292 effectiveUserPrivileges))
1293 {
1294 asyncResp->res
1295 .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&
1301 propertiesList) {
1302 if (ec)
1303 {
1304 messages::internalError(asyncResp->res);
1305 return;
1306 }
1307 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1308 << "properties for AccountService";
1309 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
1310 property : propertiesList)
1311 {
1312 if (property.first == "MinPasswordLength")
1313 {
1314 const uint8_t* value =
1315 std::get_if<uint8_t>(&property.second);
1316 if (value != nullptr)
1317 {
1318 asyncResp->res.jsonValue["MinPasswordLength"] = *value;
1319 }
1320 }
1321 if (property.first == "AccountUnlockTimeout")
1322 {
1323 const uint32_t* value =
1324 std::get_if<uint32_t>(&property.second);
1325 if (value != nullptr)
1326 {
1327 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1328 *value;
1329 }
1330 }
1331 if (property.first == "MaxLoginAttemptBeforeLockout")
1332 {
1333 const uint16_t* value =
1334 std::get_if<uint16_t>(&property.second);
1335 if (value != nullptr)
1336 {
1337 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1338 *value;
1339 }
1340 }
1341 }
1342 },
1343 "xyz.openbmc_project.User.Manager",
1344 "/xyz/openbmc_project/user",
1345 "org.freedesktop.DBus.Properties", "GetAll",
1346 "xyz.openbmc_project.User.AccountPolicy");
1347
1348 auto callback =
1349 [asyncResp](bool success, LDAPConfigData& confData,
1350 const std::string& ldapType) {
1351 if (!success)
1352 {
1353 return;
1354 }
1355 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1356 };
1357
1358 getLDAPConfigData("LDAP", callback);
1359 getLDAPConfigData("ActiveDirectory", callback);
1360 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001361
Ed Tanousf5ffd802021-07-19 10:55:33 -07001362 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06001363 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07001364 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001365 [&app](
1366 const crow::Request& req,
1367 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1368 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1369 {
1370 return;
1371 }
Ed Tanousf5ffd802021-07-19 10:55:33 -07001372 std::optional<uint32_t> unlockTimeout;
1373 std::optional<uint16_t> lockoutThreshold;
Paul Fertseref73ad02022-01-21 19:44:40 +00001374 std::optional<uint8_t> minPasswordLength;
Ed Tanousf5ffd802021-07-19 10:55:33 -07001375 std::optional<uint16_t> maxPasswordLength;
1376 std::optional<nlohmann::json> ldapObject;
1377 std::optional<nlohmann::json> activeDirectoryObject;
1378 std::optional<nlohmann::json> oemObject;
1379
Willy Tu15ed6782021-12-14 11:03:16 -08001380 if (!json_util::readJsonPatch(
Ed Tanousf5ffd802021-07-19 10:55:33 -07001381 req, asyncResp->res, "AccountLockoutDuration",
1382 unlockTimeout, "AccountLockoutThreshold",
1383 lockoutThreshold, "MaxPasswordLength",
1384 maxPasswordLength, "MinPasswordLength",
1385 minPasswordLength, "LDAP", ldapObject,
1386 "ActiveDirectory", activeDirectoryObject, "Oem",
1387 oemObject))
1388 {
1389 return;
1390 }
1391
1392 if (minPasswordLength)
1393 {
Paul Fertseref73ad02022-01-21 19:44:40 +00001394 crow::connections::systemBus->async_method_call(
1395 [asyncResp](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001396 if (ec)
1397 {
1398 messages::internalError(asyncResp->res);
1399 return;
1400 }
1401 messages::success(asyncResp->res);
Paul Fertseref73ad02022-01-21 19:44:40 +00001402 },
1403 "xyz.openbmc_project.User.Manager",
1404 "/xyz/openbmc_project/user",
1405 "org.freedesktop.DBus.Properties", "Set",
1406 "xyz.openbmc_project.User.AccountPolicy",
1407 "MinPasswordLength",
1408 dbus::utility::DbusVariantType(*minPasswordLength));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001409 }
1410
1411 if (maxPasswordLength)
1412 {
1413 messages::propertyNotWritable(asyncResp->res,
1414 "MaxPasswordLength");
1415 }
1416
1417 if (ldapObject)
1418 {
1419 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1420 }
1421
1422 if (std::optional<nlohmann::json> oemOpenBMCObject;
1423 oemObject &&
1424 json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1425 oemOpenBMCObject))
1426 {
1427 if (std::optional<nlohmann::json> authMethodsObject;
1428 oemOpenBMCObject &&
1429 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1430 "AuthMethods", authMethodsObject))
1431 {
1432 if (authMethodsObject)
1433 {
1434 handleAuthMethodsPatch(*authMethodsObject,
1435 asyncResp);
1436 }
1437 }
1438 }
1439
1440 if (activeDirectoryObject)
1441 {
1442 handleLDAPPatch(*activeDirectoryObject, asyncResp,
1443 "ActiveDirectory");
1444 }
1445
1446 if (unlockTimeout)
1447 {
1448 crow::connections::systemBus->async_method_call(
1449 [asyncResp](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001450 if (ec)
1451 {
1452 messages::internalError(asyncResp->res);
1453 return;
1454 }
1455 messages::success(asyncResp->res);
Ed Tanousf5ffd802021-07-19 10:55:33 -07001456 },
1457 "xyz.openbmc_project.User.Manager",
1458 "/xyz/openbmc_project/user",
1459 "org.freedesktop.DBus.Properties", "Set",
1460 "xyz.openbmc_project.User.AccountPolicy",
1461 "AccountUnlockTimeout",
Ed Tanous168e20c2021-12-13 14:39:53 -08001462 dbus::utility::DbusVariantType(*unlockTimeout));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001463 }
1464 if (lockoutThreshold)
1465 {
1466 crow::connections::systemBus->async_method_call(
1467 [asyncResp](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001468 if (ec)
1469 {
1470 messages::internalError(asyncResp->res);
1471 return;
1472 }
1473 messages::success(asyncResp->res);
Ed Tanousf5ffd802021-07-19 10:55:33 -07001474 },
1475 "xyz.openbmc_project.User.Manager",
1476 "/xyz/openbmc_project/user",
1477 "org.freedesktop.DBus.Properties", "Set",
1478 "xyz.openbmc_project.User.AccountPolicy",
1479 "MaxLoginAttemptBeforeLockout",
Ed Tanous168e20c2021-12-13 14:39:53 -08001480 dbus::utility::DbusVariantType(*lockoutThreshold));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001481 }
1482 });
1483
Ed Tanous6c51eab2021-06-03 12:30:29 -07001484 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001485 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001486 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001487 [&app](
1488 const crow::Request& req,
1489 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1490 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1491 {
1492 return;
1493 }
Ed Tanous14766872022-03-15 10:44:42 -07001494
1495 asyncResp->res.jsonValue["@odata.id"] =
1496 "/redfish/v1/AccountService/Accounts";
1497 asyncResp->res.jsonValue["@odata.type"] =
1498 "#ManagerAccountCollection."
1499 "ManagerAccountCollection";
1500 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1501 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001502
Ed Tanous6c51eab2021-06-03 12:30:29 -07001503 Privileges effectiveUserPrivileges =
1504 redfish::getUserPrivileges(req.userRole);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001505
JunLin Chenf5e29f32021-12-08 16:47:04 +08001506 std::string thisUser;
1507 if (req.session)
1508 {
1509 thisUser = req.session->username;
1510 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001511 crow::connections::systemBus->async_method_call(
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001512 [asyncResp, thisUser, effectiveUserPrivileges](
1513 const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001514 const dbus::utility::ManagedObjectType& users) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001515 if (ec)
1516 {
1517 messages::internalError(asyncResp->res);
1518 return;
1519 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001520
Ed Tanous002d39b2022-05-31 08:59:27 -07001521 bool userCanSeeAllAccounts =
1522 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001523
Ed Tanous002d39b2022-05-31 08:59:27 -07001524 bool userCanSeeSelf =
1525 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001526
Ed Tanous002d39b2022-05-31 08:59:27 -07001527 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1528 memberArray = nlohmann::json::array();
Ratan Gupta24c85422019-01-30 19:41:24 +05301529
Ed Tanous002d39b2022-05-31 08:59:27 -07001530 for (const auto& userpath : users)
1531 {
1532 std::string user = userpath.first.filename();
1533 if (user.empty())
1534 {
1535 messages::internalError(asyncResp->res);
1536 BMCWEB_LOG_ERROR << "Invalid firmware ID";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001537
Ed Tanous002d39b2022-05-31 08:59:27 -07001538 return;
1539 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301540
Ed Tanous002d39b2022-05-31 08:59:27 -07001541 // As clarified by Redfish here:
1542 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1543 // Users without ConfigureUsers, only see their own
1544 // account. Users with ConfigureUsers, see all
1545 // accounts.
1546 if (userCanSeeAllAccounts ||
1547 (thisUser == user && userCanSeeSelf))
1548 {
1549 nlohmann::json::object_t member;
1550 member["@odata.id"] =
1551 "/redfish/v1/AccountService/Accounts/" + user;
1552 memberArray.push_back(std::move(member));
1553 }
1554 }
1555 asyncResp->res.jsonValue["Members@odata.count"] =
1556 memberArray.size();
Ed Tanous6c51eab2021-06-03 12:30:29 -07001557 },
1558 "xyz.openbmc_project.User.Manager",
1559 "/xyz/openbmc_project/user",
1560 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Ratan Gupta24c85422019-01-30 19:41:24 +05301561 });
Ed Tanous06e086d2018-09-19 17:19:52 -07001562
Ed Tanous6c51eab2021-06-03 12:30:29 -07001563 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001564 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001565 .methods(boost::beast::http::verb::post)(
1566 [&app](
1567 const crow::Request& req,
1568 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1569 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1570 {
1571 return;
1572 }
1573 std::string username;
1574 std::string password;
1575 std::optional<std::string> roleId("User");
1576 std::optional<bool> enabled = true;
1577 if (!json_util::readJsonPatch(
1578 req, asyncResp->res, "UserName", username, "Password",
1579 password, "RoleId", roleId, "Enabled", enabled))
1580 {
1581 return;
1582 }
1583
1584 std::string priv = getPrivilegeFromRoleId(*roleId);
1585 if (priv.empty())
1586 {
1587 messages::propertyValueNotInList(asyncResp->res, *roleId,
1588 "RoleId");
1589 return;
1590 }
1591 // TODO: Following override will be reverted once support in
1592 // phosphor-user-manager is added. In order to avoid dependency
1593 // issues, this is added in bmcweb, which will removed, once
1594 // phosphor-user-manager supports priv-noaccess.
1595 if (priv == "priv-noaccess")
1596 {
1597 roleId = "";
1598 }
1599 else
1600 {
1601 roleId = priv;
1602 }
1603
1604 // Reading AllGroups property
1605 sdbusplus::asio::getProperty<std::vector<std::string>>(
1606 *crow::connections::systemBus,
1607 "xyz.openbmc_project.User.Manager",
1608 "/xyz/openbmc_project/user",
1609 "xyz.openbmc_project.User.Manager", "AllGroups",
1610 [asyncResp, username, password{std::move(password)}, roleId,
1611 enabled](const boost::system::error_code ec,
1612 const std::vector<std::string>& allGroupsList) {
1613 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001614 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001615 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1616 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001617 return;
1618 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001619
Ed Tanous002d39b2022-05-31 08:59:27 -07001620 if (allGroupsList.empty())
JunLin Chen031514f2021-12-14 14:33:49 +08001621 {
1622 messages::internalError(asyncResp->res);
1623 return;
1624 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001625
1626 crow::connections::systemBus->async_method_call(
1627 [asyncResp, username,
1628 password](const boost::system::error_code ec2,
1629 sdbusplus::message::message& m) {
1630 if (ec2)
Ed Tanous06e086d2018-09-19 17:19:52 -07001631 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001632 userErrorMessageHandler(m.get_error(), asyncResp, username,
1633 "");
Ed Tanous6c51eab2021-06-03 12:30:29 -07001634 return;
1635 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001636
1637 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1638 {
1639 // At this point we have a user that's been
1640 // created, but the password set
1641 // failed.Something is wrong, so delete the user
1642 // that we've already created
1643 sdbusplus::message::object_path tempObjPath(
1644 rootUserDbusPath);
1645 tempObjPath /= username;
1646 const std::string userPath(tempObjPath);
1647
1648 crow::connections::systemBus->async_method_call(
1649 [asyncResp,
1650 password](const boost::system::error_code ec3) {
1651 if (ec3)
1652 {
1653 messages::internalError(asyncResp->res);
1654 return;
1655 }
1656
1657 // If password is invalid
1658 messages::propertyValueFormatError(
1659 asyncResp->res, password, "Password");
1660 },
1661 "xyz.openbmc_project.User.Manager", userPath,
1662 "xyz.openbmc_project.Object.Delete", "Delete");
1663
1664 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1665 return;
1666 }
1667
1668 messages::created(asyncResp->res);
1669 asyncResp->res.addHeader(
1670 "Location",
1671 "/redfish/v1/AccountService/Accounts/" + username);
1672 },
1673 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1674 "xyz.openbmc_project.User.Manager", "CreateUser", username,
1675 allGroupsList, *roleId, *enabled);
1676 });
1677 });
1678
1679 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
1680 .privileges(redfish::privileges::getManagerAccount)
1681 .methods(boost::beast::http::verb::get)(
1682 [&app]([[maybe_unused]] const crow::Request& req,
1683 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1684 const std::string& accountName) -> void {
1685 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1686 {
1687 return;
1688 }
1689#ifdef BMCWEB_INSECURE_DISABLE_AUTHX
1690 // If authentication is disabled, there are no user accounts
1691 messages::resourceNotFound(
1692 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1693 accountName);
1694 return;
1695
1696#endif // BMCWEB_INSECURE_DISABLE_AUTHX
1697 if (req.session == nullptr)
1698 {
1699 messages::internalError(asyncResp->res);
1700 return;
1701 }
1702 if (req.session->username != accountName)
1703 {
1704 // At this point we've determined that the user is trying to
1705 // modify a user that isn't them. We need to verify that
1706 // they have permissions to modify other users, so re-run
1707 // the auth check with the same permissions, minus
1708 // ConfigureSelf.
1709 Privileges effectiveUserPrivileges =
1710 redfish::getUserPrivileges(req.userRole);
1711 Privileges requiredPermissionsToChangeNonSelf = {
1712 "ConfigureUsers", "ConfigureManager"};
1713 if (!effectiveUserPrivileges.isSupersetOf(
1714 requiredPermissionsToChangeNonSelf))
1715 {
1716 BMCWEB_LOG_DEBUG << "GET Account denied access";
1717 messages::insufficientPrivilege(asyncResp->res);
1718 return;
1719 }
1720 }
1721
1722 crow::connections::systemBus->async_method_call(
1723 [asyncResp, accountName](
1724 const boost::system::error_code ec,
1725 const dbus::utility::ManagedObjectType& users) {
1726 if (ec)
1727 {
1728 messages::internalError(asyncResp->res);
1729 return;
1730 }
1731 const auto userIt = std::find_if(
1732 users.begin(), users.end(),
1733 [accountName](
1734 const std::pair<sdbusplus::message::object_path,
1735 dbus::utility::DBusInteracesMap>& user) {
1736 return accountName == user.first.filename();
1737 });
1738
1739 if (userIt == users.end())
1740 {
1741 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1742 accountName);
1743 return;
1744 }
1745
1746 asyncResp->res.jsonValue["@odata.type"] =
1747 "#ManagerAccount.v1_4_0.ManagerAccount";
1748 asyncResp->res.jsonValue["Name"] = "User Account";
1749 asyncResp->res.jsonValue["Description"] = "User Account";
1750 asyncResp->res.jsonValue["Password"] = nullptr;
1751 asyncResp->res.jsonValue["AccountTypes"] = {"Redfish"};
1752
1753 for (const auto& interface : userIt->second)
1754 {
1755 if (interface.first == "xyz.openbmc_project.User.Attributes")
1756 {
1757 for (const auto& property : interface.second)
1758 {
1759 if (property.first == "UserEnabled")
1760 {
1761 const bool* userEnabled =
1762 std::get_if<bool>(&property.second);
1763 if (userEnabled == nullptr)
1764 {
1765 BMCWEB_LOG_ERROR << "UserEnabled wasn't a bool";
1766 messages::internalError(asyncResp->res);
1767 return;
1768 }
1769 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
1770 }
1771 else if (property.first == "UserLockedForFailedAttempt")
1772 {
1773 const bool* userLocked =
1774 std::get_if<bool>(&property.second);
1775 if (userLocked == nullptr)
1776 {
1777 BMCWEB_LOG_ERROR << "UserLockedForF"
1778 "ailedAttempt "
1779 "wasn't a bool";
1780 messages::internalError(asyncResp->res);
1781 return;
1782 }
1783 asyncResp->res.jsonValue["Locked"] = *userLocked;
1784 asyncResp->res
1785 .jsonValue["Locked@Redfish.AllowableValues"] = {
1786 "false"}; // can only unlock accounts
1787 }
1788 else if (property.first == "UserPrivilege")
1789 {
1790 const std::string* userPrivPtr =
1791 std::get_if<std::string>(&property.second);
1792 if (userPrivPtr == nullptr)
1793 {
1794 BMCWEB_LOG_ERROR << "UserPrivilege wasn't a "
1795 "string";
1796 messages::internalError(asyncResp->res);
1797 return;
1798 }
1799 std::string role =
1800 getRoleIdFromPrivilege(*userPrivPtr);
1801 if (role.empty())
1802 {
1803 BMCWEB_LOG_ERROR << "Invalid user role";
1804 messages::internalError(asyncResp->res);
1805 return;
1806 }
1807 asyncResp->res.jsonValue["RoleId"] = role;
1808
1809 nlohmann::json& roleEntry =
1810 asyncResp->res.jsonValue["Links"]["Role"];
1811 roleEntry["@odata.id"] =
1812 "/redfish/v1/AccountService/Roles/" + role;
1813 }
1814 else if (property.first == "UserPasswordExpired")
1815 {
1816 const bool* userPasswordExpired =
1817 std::get_if<bool>(&property.second);
1818 if (userPasswordExpired == nullptr)
1819 {
1820 BMCWEB_LOG_ERROR
1821 << "UserPasswordExpired wasn't a bool";
1822 messages::internalError(asyncResp->res);
1823 return;
1824 }
1825 asyncResp->res.jsonValue["PasswordChangeRequired"] =
1826 *userPasswordExpired;
1827 }
1828 }
1829 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001830 }
1831
Ed Tanous002d39b2022-05-31 08:59:27 -07001832 asyncResp->res.jsonValue["@odata.id"] =
1833 "/redfish/v1/AccountService/Accounts/" + accountName;
1834 asyncResp->res.jsonValue["Id"] = accountName;
1835 asyncResp->res.jsonValue["UserName"] = accountName;
1836 },
1837 "xyz.openbmc_project.User.Manager",
1838 "/xyz/openbmc_project/user",
1839 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1840 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001841
1842 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001843 // TODO this privilege should be using the generated endpoints, but
1844 // because of the special handling of ConfigureSelf, it's not able to
1845 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001846 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1847 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001848 [&app](const crow::Request& req,
1849 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1850 const std::string& username) -> void {
1851 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1852 {
1853 return;
1854 }
Nan Zhoua43ea822022-05-27 00:42:44 +00001855#ifdef BMCWEB_INSECURE_DISABLE_AUTHX
JunLin Chen031514f2021-12-14 14:33:49 +08001856 // If authentication is disabled, there are no user accounts
1857 messages::resourceNotFound(
1858 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1859 username);
1860 return;
1861
Nan Zhoua43ea822022-05-27 00:42:44 +00001862#endif // BMCWEB_INSECURE_DISABLE_AUTHX
Ed Tanous6c51eab2021-06-03 12:30:29 -07001863 std::optional<std::string> newUserName;
1864 std::optional<std::string> password;
1865 std::optional<bool> enabled;
1866 std::optional<std::string> roleId;
1867 std::optional<bool> locked;
Ed Tanouse9cc5172021-11-03 14:13:19 +08001868
JunLin Chen031514f2021-12-14 14:33:49 +08001869 if (req.session == nullptr)
1870 {
1871 messages::internalError(asyncResp->res);
1872 return;
1873 }
1874
Ed Tanouse9cc5172021-11-03 14:13:19 +08001875 Privileges effectiveUserPrivileges =
1876 redfish::getUserPrivileges(req.userRole);
1877 Privileges configureUsers = {"ConfigureUsers"};
1878 bool userHasConfigureUsers =
1879 effectiveUserPrivileges.isSupersetOf(configureUsers);
1880 if (userHasConfigureUsers)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001881 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001882 // Users with ConfigureUsers can modify for all users
Willy Tu15ed6782021-12-14 11:03:16 -08001883 if (!json_util::readJsonPatch(
1884 req, asyncResp->res, "UserName", newUserName,
1885 "Password", password, "RoleId", roleId, "Enabled",
1886 enabled, "Locked", locked))
Ed Tanouse9cc5172021-11-03 14:13:19 +08001887 {
1888 return;
1889 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001890 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001891 else
Ed Tanous6c51eab2021-06-03 12:30:29 -07001892 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001893 // ConfigureSelf accounts can only modify their own account
1894 if (username != req.session->username)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001895 {
1896 messages::insufficientPrivilege(asyncResp->res);
1897 return;
1898 }
JunLin Chen031514f2021-12-14 14:33:49 +08001899
Ed Tanouse9cc5172021-11-03 14:13:19 +08001900 // ConfigureSelf accounts can only modify their password
Willy Tu15ed6782021-12-14 11:03:16 -08001901 if (!json_util::readJsonPatch(req, asyncResp->res,
1902 "Password", password))
Ed Tanouse9cc5172021-11-03 14:13:19 +08001903 {
1904 return;
1905 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001906 }
1907
1908 // if user name is not provided in the patch method or if it
1909 // matches the user name in the URI, then we are treating it as
1910 // updating user properties other then username. If username
1911 // provided doesn't match the URI, then we are treating this as
1912 // user rename request.
1913 if (!newUserName || (newUserName.value() == username))
1914 {
1915 updateUserProperties(asyncResp, username, password, enabled,
1916 roleId, locked);
1917 return;
1918 }
1919 crow::connections::systemBus->async_method_call(
1920 [asyncResp, username, password(std::move(password)),
1921 roleId(std::move(roleId)), enabled,
1922 newUser{std::string(*newUserName)},
1923 locked](const boost::system::error_code ec,
1924 sdbusplus::message::message& m) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001925 if (ec)
1926 {
1927 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
1928 username);
1929 return;
1930 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001931
Ed Tanous002d39b2022-05-31 08:59:27 -07001932 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
1933 locked);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001934 },
1935 "xyz.openbmc_project.User.Manager",
1936 "/xyz/openbmc_project/user",
1937 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1938 *newUserName);
1939 });
1940
1941 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001942 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001943 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001944 [&app](const crow::Request& req,
1945 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1946 const std::string& username) -> void {
1947 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1948 {
1949 return;
1950 }
JunLin Chen031514f2021-12-14 14:33:49 +08001951
Nan Zhoua43ea822022-05-27 00:42:44 +00001952#ifdef BMCWEB_INSECURE_DISABLE_AUTHX
JunLin Chen031514f2021-12-14 14:33:49 +08001953 // If authentication is disabled, there are no user accounts
1954 messages::resourceNotFound(
1955 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1956 username);
1957 return;
1958
Nan Zhoua43ea822022-05-27 00:42:44 +00001959#endif // BMCWEB_INSECURE_DISABLE_AUTHX
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301960 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1961 tempObjPath /= username;
1962 const std::string userPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001963
1964 crow::connections::systemBus->async_method_call(
1965 [asyncResp, username](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001966 if (ec)
1967 {
1968 messages::resourceNotFound(
1969 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1970 username);
1971 return;
1972 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001973
Ed Tanous002d39b2022-05-31 08:59:27 -07001974 messages::accountRemoved(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001975 },
1976 "xyz.openbmc_project.User.Manager", userPath,
1977 "xyz.openbmc_project.Object.Delete", "Delete");
1978 });
1979}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001980
Ed Tanous1abe55e2018-09-05 08:30:59 -07001981} // namespace redfish