blob: f8ba322566f2d83abb063d7ac7f8f412c456ce4a [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 Tanous1ef4c342022-05-12 16:12:36 -07001241inline void
1242 handleAccountServiceGet(App& app, const crow::Request& req,
1243 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1244{
1245 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1246 {
1247 return;
1248 }
1249 const persistent_data::AuthConfigMethods& authMethodsConfig =
1250 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1251
1252 nlohmann::json& json = asyncResp->res.jsonValue;
1253 json["@odata.id"] = "/redfish/v1/AccountService";
1254 json["@odata.type"] = "#AccountService."
1255 "v1_10_0.AccountService";
1256 json["Id"] = "AccountService";
1257 json["Name"] = "Account Service";
1258 json["Description"] = "Account Service";
1259 json["ServiceEnabled"] = true;
1260 json["MaxPasswordLength"] = 20;
1261 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1262 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
1263 json["Oem"]["OpenBMC"]["@odata.type"] =
1264 "#OemAccountService.v1_0_0.AccountService";
1265 json["Oem"]["OpenBMC"]["@odata.id"] =
1266 "/redfish/v1/AccountService#/Oem/OpenBMC";
1267 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1268 authMethodsConfig.basic;
1269 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1270 authMethodsConfig.sessionToken;
1271 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1272 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1273 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1274
1275 // /redfish/v1/AccountService/LDAP/Certificates is something only
1276 // ConfigureManager can access then only display when the user has
1277 // permissions ConfigureManager
1278 Privileges effectiveUserPrivileges =
1279 redfish::getUserPrivileges(req.userRole);
1280
1281 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1282 effectiveUserPrivileges))
1283 {
1284 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1285 "/redfish/v1/AccountService/LDAP/Certificates";
1286 }
1287 crow::connections::systemBus->async_method_call(
1288 [asyncResp](const boost::system::error_code ec,
1289 const dbus::utility::DBusPropertiesMap& propertiesList) {
1290 if (ec)
1291 {
1292 messages::internalError(asyncResp->res);
1293 return;
1294 }
1295 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1296 << "properties for AccountService";
1297 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
1298 property : propertiesList)
1299 {
1300 if (property.first == "MinPasswordLength")
1301 {
1302 const uint8_t* value = std::get_if<uint8_t>(&property.second);
1303 if (value != nullptr)
1304 {
1305 asyncResp->res.jsonValue["MinPasswordLength"] = *value;
1306 }
1307 }
1308 if (property.first == "AccountUnlockTimeout")
1309 {
1310 const uint32_t* value = std::get_if<uint32_t>(&property.second);
1311 if (value != nullptr)
1312 {
1313 asyncResp->res.jsonValue["AccountLockoutDuration"] = *value;
1314 }
1315 }
1316 if (property.first == "MaxLoginAttemptBeforeLockout")
1317 {
1318 const uint16_t* value = std::get_if<uint16_t>(&property.second);
1319 if (value != nullptr)
1320 {
1321 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1322 *value;
1323 }
1324 }
1325 }
1326 },
1327 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1328 "org.freedesktop.DBus.Properties", "GetAll",
1329 "xyz.openbmc_project.User.AccountPolicy");
1330
Ed Tanous02cad962022-06-30 16:50:15 -07001331 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001332 const std::string& ldapType) {
1333 if (!success)
1334 {
1335 return;
1336 }
1337 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1338 };
1339
1340 getLDAPConfigData("LDAP", callback);
1341 getLDAPConfigData("ActiveDirectory", callback);
1342}
1343
1344inline void handleAccountServicePatch(
1345 App& app, const crow::Request& req,
1346 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1347{
1348 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1349 {
1350 return;
1351 }
1352 std::optional<uint32_t> unlockTimeout;
1353 std::optional<uint16_t> lockoutThreshold;
1354 std::optional<uint8_t> minPasswordLength;
1355 std::optional<uint16_t> maxPasswordLength;
1356 std::optional<nlohmann::json> ldapObject;
1357 std::optional<nlohmann::json> activeDirectoryObject;
1358 std::optional<nlohmann::json> oemObject;
1359
1360 if (!json_util::readJsonPatch(
1361 req, asyncResp->res, "AccountLockoutDuration", unlockTimeout,
1362 "AccountLockoutThreshold", lockoutThreshold, "MaxPasswordLength",
1363 maxPasswordLength, "MinPasswordLength", minPasswordLength, "LDAP",
1364 ldapObject, "ActiveDirectory", activeDirectoryObject, "Oem",
1365 oemObject))
1366 {
1367 return;
1368 }
1369
1370 if (minPasswordLength)
1371 {
1372 crow::connections::systemBus->async_method_call(
1373 [asyncResp](const boost::system::error_code ec) {
1374 if (ec)
1375 {
1376 messages::internalError(asyncResp->res);
1377 return;
1378 }
1379 messages::success(asyncResp->res);
1380 },
1381 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1382 "org.freedesktop.DBus.Properties", "Set",
1383 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
1384 dbus::utility::DbusVariantType(*minPasswordLength));
1385 }
1386
1387 if (maxPasswordLength)
1388 {
1389 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1390 }
1391
1392 if (ldapObject)
1393 {
1394 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1395 }
1396
1397 if (std::optional<nlohmann::json> oemOpenBMCObject;
1398 oemObject && json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1399 oemOpenBMCObject))
1400 {
1401 if (std::optional<nlohmann::json> authMethodsObject;
1402 oemOpenBMCObject &&
1403 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1404 "AuthMethods", authMethodsObject))
1405 {
1406 if (authMethodsObject)
1407 {
1408 handleAuthMethodsPatch(*authMethodsObject, asyncResp);
1409 }
1410 }
1411 }
1412
1413 if (activeDirectoryObject)
1414 {
1415 handleLDAPPatch(*activeDirectoryObject, asyncResp, "ActiveDirectory");
1416 }
1417
1418 if (unlockTimeout)
1419 {
1420 crow::connections::systemBus->async_method_call(
1421 [asyncResp](const boost::system::error_code ec) {
1422 if (ec)
1423 {
1424 messages::internalError(asyncResp->res);
1425 return;
1426 }
1427 messages::success(asyncResp->res);
1428 },
1429 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1430 "org.freedesktop.DBus.Properties", "Set",
1431 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
1432 dbus::utility::DbusVariantType(*unlockTimeout));
1433 }
1434 if (lockoutThreshold)
1435 {
1436 crow::connections::systemBus->async_method_call(
1437 [asyncResp](const boost::system::error_code ec) {
1438 if (ec)
1439 {
1440 messages::internalError(asyncResp->res);
1441 return;
1442 }
1443 messages::success(asyncResp->res);
1444 },
1445 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1446 "org.freedesktop.DBus.Properties", "Set",
1447 "xyz.openbmc_project.User.AccountPolicy",
1448 "MaxLoginAttemptBeforeLockout",
1449 dbus::utility::DbusVariantType(*lockoutThreshold));
1450 }
1451}
1452
1453inline void handleAccountCollectionGet(
1454 App& app, const crow::Request& req,
1455 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1456{
1457 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1458 {
1459 return;
1460 }
1461
1462 asyncResp->res.jsonValue["@odata.id"] =
1463 "/redfish/v1/AccountService/Accounts";
1464 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1465 "ManagerAccountCollection";
1466 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1467 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1468
1469 Privileges effectiveUserPrivileges =
1470 redfish::getUserPrivileges(req.userRole);
1471
1472 std::string thisUser;
1473 if (req.session)
1474 {
1475 thisUser = req.session->username;
1476 }
1477 crow::connections::systemBus->async_method_call(
1478 [asyncResp, thisUser, effectiveUserPrivileges](
1479 const boost::system::error_code ec,
1480 const dbus::utility::ManagedObjectType& users) {
1481 if (ec)
1482 {
1483 messages::internalError(asyncResp->res);
1484 return;
1485 }
1486
1487 bool userCanSeeAllAccounts =
1488 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1489
1490 bool userCanSeeSelf =
1491 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1492
1493 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1494 memberArray = nlohmann::json::array();
1495
1496 for (const auto& userpath : users)
1497 {
1498 std::string user = userpath.first.filename();
1499 if (user.empty())
1500 {
1501 messages::internalError(asyncResp->res);
1502 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1503
1504 return;
1505 }
1506
1507 // As clarified by Redfish here:
1508 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1509 // Users without ConfigureUsers, only see their own
1510 // account. Users with ConfigureUsers, see all
1511 // accounts.
1512 if (userCanSeeAllAccounts || (thisUser == user && userCanSeeSelf))
1513 {
1514 nlohmann::json::object_t member;
1515 member["@odata.id"] =
1516 "/redfish/v1/AccountService/Accounts/" + user;
1517 memberArray.push_back(std::move(member));
1518 }
1519 }
1520 asyncResp->res.jsonValue["Members@odata.count"] = memberArray.size();
1521 },
1522 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1523 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1524}
1525
1526inline void handleAccountCollectionPost(
1527 App& app, const crow::Request& req,
1528 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1529{
1530 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1531 {
1532 return;
1533 }
1534 std::string username;
1535 std::string password;
1536 std::optional<std::string> roleId("User");
1537 std::optional<bool> enabled = true;
1538 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", username,
1539 "Password", password, "RoleId", roleId,
1540 "Enabled", enabled))
1541 {
1542 return;
1543 }
1544
1545 std::string priv = getPrivilegeFromRoleId(*roleId);
1546 if (priv.empty())
1547 {
1548 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
1549 return;
1550 }
1551 // TODO: Following override will be reverted once support in
1552 // phosphor-user-manager is added. In order to avoid dependency
1553 // issues, this is added in bmcweb, which will removed, once
1554 // phosphor-user-manager supports priv-noaccess.
1555 if (priv == "priv-noaccess")
1556 {
1557 roleId = "";
1558 }
1559 else
1560 {
1561 roleId = priv;
1562 }
1563
1564 // Reading AllGroups property
1565 sdbusplus::asio::getProperty<std::vector<std::string>>(
1566 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1567 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager",
1568 "AllGroups",
1569 [asyncResp, username, password{std::move(password)}, roleId,
1570 enabled](const boost::system::error_code ec,
1571 const std::vector<std::string>& allGroupsList) {
1572 if (ec)
1573 {
1574 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1575 messages::internalError(asyncResp->res);
1576 return;
1577 }
1578
1579 if (allGroupsList.empty())
1580 {
1581 messages::internalError(asyncResp->res);
1582 return;
1583 }
1584
1585 crow::connections::systemBus->async_method_call(
1586 [asyncResp, username, password](const boost::system::error_code ec2,
1587 sdbusplus::message::message& m) {
1588 if (ec2)
1589 {
1590 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1591 return;
1592 }
1593
1594 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1595 {
1596 // At this point we have a user that's been
1597 // created, but the password set
1598 // failed.Something is wrong, so delete the user
1599 // that we've already created
1600 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1601 tempObjPath /= username;
1602 const std::string userPath(tempObjPath);
1603
1604 crow::connections::systemBus->async_method_call(
1605 [asyncResp, password](const boost::system::error_code ec3) {
1606 if (ec3)
1607 {
1608 messages::internalError(asyncResp->res);
1609 return;
1610 }
1611
1612 // If password is invalid
1613 messages::propertyValueFormatError(asyncResp->res, password,
1614 "Password");
1615 },
1616 "xyz.openbmc_project.User.Manager", userPath,
1617 "xyz.openbmc_project.Object.Delete", "Delete");
1618
1619 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1620 return;
1621 }
1622
1623 messages::created(asyncResp->res);
1624 asyncResp->res.addHeader(
1625 "Location", "/redfish/v1/AccountService/Accounts/" + username);
1626 },
1627 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1628 "xyz.openbmc_project.User.Manager", "CreateUser", username,
1629 allGroupsList, *roleId, *enabled);
1630 });
1631}
1632
1633inline void
1634 handleAccountGet(App& app, const crow::Request& req,
1635 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1636 const std::string& accountName)
1637{
1638 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1639 {
1640 return;
1641 }
1642#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1643 // If authentication is disabled, there are no user accounts
1644 messages::resourceNotFound(
1645 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", accountName);
1646 return;
1647
1648#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1649 if (req.session == nullptr)
1650 {
1651 messages::internalError(asyncResp->res);
1652 return;
1653 }
1654 if (req.session->username != accountName)
1655 {
1656 // At this point we've determined that the user is trying to
1657 // modify a user that isn't them. We need to verify that they
1658 // have permissions to modify other users, so re-run the auth
1659 // check with the same permissions, minus ConfigureSelf.
1660 Privileges effectiveUserPrivileges =
1661 redfish::getUserPrivileges(req.userRole);
1662 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
1663 "ConfigureManager"};
1664 if (!effectiveUserPrivileges.isSupersetOf(
1665 requiredPermissionsToChangeNonSelf))
1666 {
1667 BMCWEB_LOG_DEBUG << "GET Account denied access";
1668 messages::insufficientPrivilege(asyncResp->res);
1669 return;
1670 }
1671 }
1672
1673 crow::connections::systemBus->async_method_call(
1674 [asyncResp,
1675 accountName](const boost::system::error_code ec,
1676 const dbus::utility::ManagedObjectType& users) {
1677 if (ec)
1678 {
1679 messages::internalError(asyncResp->res);
1680 return;
1681 }
1682 const auto userIt = std::find_if(
1683 users.begin(), users.end(),
1684 [accountName](
1685 const std::pair<sdbusplus::message::object_path,
1686 dbus::utility::DBusInteracesMap>& user) {
1687 return accountName == user.first.filename();
1688 });
1689
1690 if (userIt == users.end())
1691 {
1692 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1693 accountName);
1694 return;
1695 }
1696
1697 asyncResp->res.jsonValue["@odata.type"] =
1698 "#ManagerAccount.v1_4_0.ManagerAccount";
1699 asyncResp->res.jsonValue["Name"] = "User Account";
1700 asyncResp->res.jsonValue["Description"] = "User Account";
1701 asyncResp->res.jsonValue["Password"] = nullptr;
1702 asyncResp->res.jsonValue["AccountTypes"] = {"Redfish"};
1703
1704 for (const auto& interface : userIt->second)
1705 {
1706 if (interface.first == "xyz.openbmc_project.User.Attributes")
1707 {
1708 for (const auto& property : interface.second)
1709 {
1710 if (property.first == "UserEnabled")
1711 {
1712 const bool* userEnabled =
1713 std::get_if<bool>(&property.second);
1714 if (userEnabled == nullptr)
1715 {
1716 BMCWEB_LOG_ERROR << "UserEnabled wasn't a bool";
1717 messages::internalError(asyncResp->res);
1718 return;
1719 }
1720 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
1721 }
1722 else if (property.first == "UserLockedForFailedAttempt")
1723 {
1724 const bool* userLocked =
1725 std::get_if<bool>(&property.second);
1726 if (userLocked == nullptr)
1727 {
1728 BMCWEB_LOG_ERROR << "UserLockedForF"
1729 "ailedAttempt "
1730 "wasn't a bool";
1731 messages::internalError(asyncResp->res);
1732 return;
1733 }
1734 asyncResp->res.jsonValue["Locked"] = *userLocked;
1735 asyncResp->res
1736 .jsonValue["Locked@Redfish.AllowableValues"] = {
1737 "false"}; // can only unlock accounts
1738 }
1739 else if (property.first == "UserPrivilege")
1740 {
1741 const std::string* userPrivPtr =
1742 std::get_if<std::string>(&property.second);
1743 if (userPrivPtr == nullptr)
1744 {
1745 BMCWEB_LOG_ERROR << "UserPrivilege wasn't a "
1746 "string";
1747 messages::internalError(asyncResp->res);
1748 return;
1749 }
1750 std::string role = getRoleIdFromPrivilege(*userPrivPtr);
1751 if (role.empty())
1752 {
1753 BMCWEB_LOG_ERROR << "Invalid user role";
1754 messages::internalError(asyncResp->res);
1755 return;
1756 }
1757 asyncResp->res.jsonValue["RoleId"] = role;
1758
1759 nlohmann::json& roleEntry =
1760 asyncResp->res.jsonValue["Links"]["Role"];
1761 roleEntry["@odata.id"] =
1762 "/redfish/v1/AccountService/Roles/" + role;
1763 }
1764 else if (property.first == "UserPasswordExpired")
1765 {
1766 const bool* userPasswordExpired =
1767 std::get_if<bool>(&property.second);
1768 if (userPasswordExpired == nullptr)
1769 {
1770 BMCWEB_LOG_ERROR
1771 << "UserPasswordExpired wasn't a bool";
1772 messages::internalError(asyncResp->res);
1773 return;
1774 }
1775 asyncResp->res.jsonValue["PasswordChangeRequired"] =
1776 *userPasswordExpired;
1777 }
1778 }
1779 }
1780 }
1781
1782 asyncResp->res.jsonValue["@odata.id"] =
1783 "/redfish/v1/AccountService/Accounts/" + accountName;
1784 asyncResp->res.jsonValue["Id"] = accountName;
1785 asyncResp->res.jsonValue["UserName"] = accountName;
1786 },
1787 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1788 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1789}
1790
1791inline void
1792 handleAccounttDelete(App& app, const crow::Request& req,
1793 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1794 const std::string& username)
1795{
1796 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1797 {
1798 return;
1799 }
1800
1801#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1802 // If authentication is disabled, there are no user accounts
1803 messages::resourceNotFound(
1804 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
1805 return;
1806
1807#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1808 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1809 tempObjPath /= username;
1810 const std::string userPath(tempObjPath);
1811
1812 crow::connections::systemBus->async_method_call(
1813 [asyncResp, username](const boost::system::error_code ec) {
1814 if (ec)
1815 {
1816 messages::resourceNotFound(asyncResp->res,
1817 "#ManagerAccount.v1_4_0.ManagerAccount",
1818 username);
1819 return;
1820 }
1821
1822 messages::accountRemoved(asyncResp->res);
1823 },
1824 "xyz.openbmc_project.User.Manager", userPath,
1825 "xyz.openbmc_project.Object.Delete", "Delete");
1826}
1827
1828inline void
1829 handleAccountPatch(App& app, const crow::Request& req,
1830 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1831 const std::string& username)
1832{
1833 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1834 {
1835 return;
1836 }
1837#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1838 // If authentication is disabled, there are no user accounts
1839 messages::resourceNotFound(
1840 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
1841 return;
1842
1843#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1844 std::optional<std::string> newUserName;
1845 std::optional<std::string> password;
1846 std::optional<bool> enabled;
1847 std::optional<std::string> roleId;
1848 std::optional<bool> locked;
1849
1850 if (req.session == nullptr)
1851 {
1852 messages::internalError(asyncResp->res);
1853 return;
1854 }
1855
1856 Privileges effectiveUserPrivileges =
1857 redfish::getUserPrivileges(req.userRole);
1858 Privileges configureUsers = {"ConfigureUsers"};
1859 bool userHasConfigureUsers =
1860 effectiveUserPrivileges.isSupersetOf(configureUsers);
1861 if (userHasConfigureUsers)
1862 {
1863 // Users with ConfigureUsers can modify for all users
1864 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
1865 newUserName, "Password", password,
1866 "RoleId", roleId, "Enabled", enabled,
1867 "Locked", locked))
1868 {
1869 return;
1870 }
1871 }
1872 else
1873 {
1874 // ConfigureSelf accounts can only modify their own account
1875 if (username != req.session->username)
1876 {
1877 messages::insufficientPrivilege(asyncResp->res);
1878 return;
1879 }
1880
1881 // ConfigureSelf accounts can only modify their password
1882 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
1883 password))
1884 {
1885 return;
1886 }
1887 }
1888
1889 // if user name is not provided in the patch method or if it
1890 // matches the user name in the URI, then we are treating it as
1891 // updating user properties other then username. If username
1892 // provided doesn't match the URI, then we are treating this as
1893 // user rename request.
1894 if (!newUserName || (newUserName.value() == username))
1895 {
1896 updateUserProperties(asyncResp, username, password, enabled, roleId,
1897 locked);
1898 return;
1899 }
1900 crow::connections::systemBus->async_method_call(
1901 [asyncResp, username, password(std::move(password)),
1902 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
1903 locked](const boost::system::error_code ec,
1904 sdbusplus::message::message& m) {
1905 if (ec)
1906 {
1907 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
1908 username);
1909 return;
1910 }
1911
1912 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
1913 locked);
1914 },
1915 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1916 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1917 *newUserName);
1918}
1919
Ed Tanous6c51eab2021-06-03 12:30:29 -07001920inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001921{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001922
Ed Tanous6c51eab2021-06-03 12:30:29 -07001923 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001924 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07001925 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001926 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001927
Ed Tanousf5ffd802021-07-19 10:55:33 -07001928 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06001929 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07001930 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001931 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001932
Ed Tanous6c51eab2021-06-03 12:30:29 -07001933 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001934 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001935 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001936 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07001937
Ed Tanous6c51eab2021-06-03 12:30:29 -07001938 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001939 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001940 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001941 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07001942
1943 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
1944 .privileges(redfish::privileges::getManagerAccount)
1945 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001946 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001947
1948 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001949 // TODO this privilege should be using the generated endpoints, but
1950 // because of the special handling of ConfigureSelf, it's not able to
1951 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001952 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1953 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001954 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001955
1956 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001957 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001958 .methods(boost::beast::http::verb::delete_)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001959 std::bind_front(handleAccounttDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001960}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001961
Ed Tanous1abe55e2018-09-05 08:30:59 -07001962} // namespace redfish