blob: 4c639ad935516f58ac3fd3b7788ddd472ab02fcd [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#include "node.hpp"
18
John Edward Broadbent7e860f12021-04-08 15:57:16 -070019#include <app.hpp>
Ratan Gupta24c85422019-01-30 19:41:24 +053020#include <dbus_utility.hpp>
Ed Tanous65b0dc32018-09-19 16:04:03 -070021#include <error_messages.hpp>
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070022#include <openbmc_dbus_rest.hpp>
Ed Tanous52cc1122020-07-18 13:51:21 -070023#include <persistent_data.hpp>
Ed Tanousa8408792018-09-05 16:08:38 -070024#include <utils/json_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050025
Ed Tanousabf2add2019-01-22 16:40:12 -080026#include <variant>
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070027
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
Ratan Gupta6973a582018-12-13 18:25:44 +053036constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
37constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
38constexpr const char* ldapConfigInterface =
39 "xyz.openbmc_project.User.Ldap.Config";
40constexpr const char* ldapCreateInterface =
41 "xyz.openbmc_project.User.Ldap.Create";
42constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053043constexpr const char* ldapPrivMapperInterface =
44 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053045constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
46constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
47constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
48constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
49constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
50
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060051struct LDAPRoleMapData
52{
53 std::string groupName;
54 std::string privilege;
55};
56
Ratan Gupta6973a582018-12-13 18:25:44 +053057struct LDAPConfigData
58{
59 std::string uri{};
60 std::string bindDN{};
61 std::string baseDN{};
62 std::string searchScope{};
63 std::string serverType{};
64 bool serviceEnabled = false;
65 std::string userNameAttribute{};
66 std::string groupAttribute{};
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060067 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053068};
69
Patrick Williams19bd78d2020-05-13 17:38:24 -050070using DbusVariantType = std::variant<bool, int32_t, std::string>;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020071
72using DbusInterfaceType = boost::container::flat_map<
73 std::string, boost::container::flat_map<std::string, DbusVariantType>>;
74
75using ManagedObjectType =
76 std::vector<std::pair<sdbusplus::message::object_path, DbusInterfaceType>>;
77
Ratan Gupta6973a582018-12-13 18:25:44 +053078using GetObjectType =
79 std::vector<std::pair<std::string, std::vector<std::string>>>;
AppaRao Puli84e12cb2018-10-11 01:28:15 +053080
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060081inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053082{
83 if (role == "priv-admin")
84 {
85 return "Administrator";
86 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070087 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053088 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053089 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053090 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070091 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053092 {
93 return "Operator";
94 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070095 if ((role == "") || (role == "priv-noaccess"))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +000096 {
97 return "NoAccess";
98 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +053099 return "";
100}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600101inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530102{
103 if (role == "Administrator")
104 {
105 return "priv-admin";
106 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700107 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530108 {
109 return "priv-user";
110 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700111 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530112 {
113 return "priv-operator";
114 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700115 if ((role == "NoAccess") || (role == ""))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +0000116 {
117 return "priv-noaccess";
118 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530119 return "";
120}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700121
zhanghch058d1b46d2021-04-01 11:18:24 +0800122inline void userErrorMessageHandler(
123 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
124 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000125{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000126 if (e == nullptr)
127 {
128 messages::internalError(asyncResp->res);
129 return;
130 }
131
Manojkiran Eda055806b2020-11-03 09:36:28 +0530132 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000133 if (strcmp(errorMessage,
134 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
135 {
136 messages::resourceAlreadyExists(asyncResp->res,
Gunnar Mills8114bd42020-06-11 20:55:21 -0500137 "#ManagerAccount.v1_4_0.ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000138 "UserName", newUser);
139 }
140 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
141 "UserNameDoesNotExist") == 0)
142 {
143 messages::resourceNotFound(
Gunnar Mills8114bd42020-06-11 20:55:21 -0500144 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000145 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700146 else if ((strcmp(errorMessage,
147 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
148 0) ||
149 (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
150 "UserNameGroupFail") == 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000151 {
152 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
153 }
154 else if (strcmp(errorMessage,
155 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
156 {
157 messages::createLimitReachedForResource(asyncResp->res);
158 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000159 else
160 {
161 messages::internalError(asyncResp->res);
162 }
163
164 return;
165}
166
Ed Tanous81ce6092020-12-17 16:54:55 +0000167inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000168 const LDAPConfigData& confData,
169 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530170{
Ratan Guptaab828d72019-04-22 14:18:33 +0530171 std::string service =
172 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600173 nlohmann::json ldap = {
Ratan Gupta6973a582018-12-13 18:25:44 +0530174 {"ServiceEnabled", confData.serviceEnabled},
175 {"ServiceAddresses", nlohmann::json::array({confData.uri})},
176 {"Authentication",
177 {{"AuthenticationType", "UsernameAndPassword"},
Ratan Gupta6973a582018-12-13 18:25:44 +0530178 {"Username", confData.bindDN},
179 {"Password", nullptr}}},
180 {"LDAPService",
181 {{"SearchSettings",
182 {{"BaseDistinguishedNames",
183 nlohmann::json::array({confData.baseDN})},
184 {"UsernameAttribute", confData.userNameAttribute},
185 {"GroupsAttribute", confData.groupAttribute}}}}},
186 };
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600187
Ed Tanous81ce6092020-12-17 16:54:55 +0000188 jsonResponse[ldapType].update(ldap);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600189
Ed Tanous81ce6092020-12-17 16:54:55 +0000190 nlohmann::json& roleMapArray = jsonResponse[ldapType]["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600191 roleMapArray = nlohmann::json::array();
192 for (auto& obj : confData.groupRoleList)
193 {
194 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
195 << obj.second.groupName << "\n";
196 roleMapArray.push_back(
197 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
198 nlohmann::json::array(
199 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
200 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530201}
202
203/**
Ratan Gupta06785242019-07-26 22:30:16 +0530204 * @brief validates given JSON input and then calls appropriate method to
205 * create, to delete or to set Rolemapping object based on the given input.
206 *
207 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000208inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800209 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530210 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700211 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530212{
213 for (size_t index = 0; index < input.size(); index++)
214 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700215 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530216
217 if (thisJson.is_null())
218 {
219 // delete the existing object
220 if (index < roleMapObjData.size())
221 {
222 crow::connections::systemBus->async_method_call(
223 [asyncResp, roleMapObjData, serverType,
224 index](const boost::system::error_code ec) {
225 if (ec)
226 {
227 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
228 messages::internalError(asyncResp->res);
229 return;
230 }
231 asyncResp->res
232 .jsonValue[serverType]["RemoteRoleMapping"][index] =
233 nullptr;
234 },
235 ldapDbusService, roleMapObjData[index].first,
236 "xyz.openbmc_project.Object.Delete", "Delete");
237 }
238 else
239 {
240 BMCWEB_LOG_ERROR << "Can't delete the object";
241 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800242 asyncResp->res,
243 thisJson.dump(2, ' ', true,
244 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530245 "RemoteRoleMapping/" + std::to_string(index));
246 return;
247 }
248 }
249 else if (thisJson.empty())
250 {
251 // Don't do anything for the empty objects,parse next json
252 // eg {"RemoteRoleMapping",[{}]}
253 }
254 else
255 {
256 // update/create the object
257 std::optional<std::string> remoteGroup;
258 std::optional<std::string> localRole;
259
Ed Tanousf23b7292020-10-15 09:41:17 -0700260 // This is a copy, but it's required in this case because of how
261 // readJson is structured
262 nlohmann::json thisJsonCopy = thisJson;
263 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
264 "RemoteGroup", remoteGroup, "LocalRole",
265 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530266 {
267 continue;
268 }
269
270 // Update existing RoleMapping Object
271 if (index < roleMapObjData.size())
272 {
273 BMCWEB_LOG_DEBUG << "Update Role Map Object";
274 // If "RemoteGroup" info is provided
275 if (remoteGroup)
276 {
277 crow::connections::systemBus->async_method_call(
278 [asyncResp, roleMapObjData, serverType, index,
279 remoteGroup](const boost::system::error_code ec) {
280 if (ec)
281 {
282 BMCWEB_LOG_ERROR << "DBUS response error: "
283 << ec;
284 messages::internalError(asyncResp->res);
285 return;
286 }
287 asyncResp->res
288 .jsonValue[serverType]["RemoteRoleMapping"]
289 [index]["RemoteGroup"] = *remoteGroup;
290 },
291 ldapDbusService, roleMapObjData[index].first,
292 propertyInterface, "Set",
293 "xyz.openbmc_project.User.PrivilegeMapperEntry",
294 "GroupName",
295 std::variant<std::string>(std::move(*remoteGroup)));
296 }
297
298 // If "LocalRole" info is provided
299 if (localRole)
300 {
301 crow::connections::systemBus->async_method_call(
302 [asyncResp, roleMapObjData, serverType, index,
303 localRole](const boost::system::error_code ec) {
304 if (ec)
305 {
306 BMCWEB_LOG_ERROR << "DBUS response error: "
307 << ec;
308 messages::internalError(asyncResp->res);
309 return;
310 }
311 asyncResp->res
312 .jsonValue[serverType]["RemoteRoleMapping"]
313 [index]["LocalRole"] = *localRole;
314 },
315 ldapDbusService, roleMapObjData[index].first,
316 propertyInterface, "Set",
317 "xyz.openbmc_project.User.PrivilegeMapperEntry",
318 "Privilege",
319 std::variant<std::string>(
320 getPrivilegeFromRoleId(std::move(*localRole))));
321 }
322 }
323 // Create a new RoleMapping Object.
324 else
325 {
326 BMCWEB_LOG_DEBUG
327 << "setRoleMappingProperties: Creating new Object";
328 std::string pathString =
329 "RemoteRoleMapping/" + std::to_string(index);
330
331 if (!localRole)
332 {
333 messages::propertyMissing(asyncResp->res,
334 pathString + "/LocalRole");
335 continue;
336 }
337 if (!remoteGroup)
338 {
339 messages::propertyMissing(asyncResp->res,
340 pathString + "/RemoteGroup");
341 continue;
342 }
343
344 std::string dbusObjectPath;
345 if (serverType == "ActiveDirectory")
346 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700347 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530348 }
349 else if (serverType == "LDAP")
350 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000351 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530352 }
353
354 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
355 << ",LocalRole=" << *localRole;
356
357 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700358 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530359 remoteGroup](const boost::system::error_code ec) {
360 if (ec)
361 {
362 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
363 messages::internalError(asyncResp->res);
364 return;
365 }
366 nlohmann::json& remoteRoleJson =
367 asyncResp->res
368 .jsonValue[serverType]["RemoteRoleMapping"];
369 remoteRoleJson.push_back(
370 {{"LocalRole", *localRole},
371 {"RemoteGroup", *remoteGroup}});
372 },
373 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700374 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530375 getPrivilegeFromRoleId(std::move(*localRole)));
376 }
377 }
378 }
379}
380
381/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530382 * Function that retrieves all properties for LDAP config object
383 * into JSON
384 */
385template <typename CallbackFunc>
386inline void getLDAPConfigData(const std::string& ldapType,
387 CallbackFunc&& callback)
388{
Ratan Guptaab828d72019-04-22 14:18:33 +0530389
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600390 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530391 ldapConfigInterface};
392
393 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600394 [callback, ldapType](const boost::system::error_code ec,
395 const GetObjectType& resp) {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600396 if (ec || resp.empty())
397 {
398 BMCWEB_LOG_ERROR << "DBUS response error during getting of "
399 "service name: "
400 << ec;
Ed Tanous23a21a12020-07-25 04:45:05 +0000401 LDAPConfigData empty{};
402 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600403 return;
404 }
405 std::string service = resp.begin()->first;
406 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000407 [callback, ldapType](const boost::system::error_code errorCode,
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600408 const ManagedObjectType& ldapObjects) {
409 LDAPConfigData confData{};
Ed Tanous81ce6092020-12-17 16:54:55 +0000410 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600411 {
412 callback(false, confData, ldapType);
413 BMCWEB_LOG_ERROR << "D-Bus responses error: "
Ed Tanous81ce6092020-12-17 16:54:55 +0000414 << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600415 return;
416 }
417
418 std::string ldapDbusType;
419 std::string searchString;
420
421 if (ldapType == "LDAP")
422 {
423 ldapDbusType = "xyz.openbmc_project.User.Ldap.Config."
424 "Type.OpenLdap";
425 searchString = "openldap";
426 }
427 else if (ldapType == "ActiveDirectory")
428 {
429 ldapDbusType =
430 "xyz.openbmc_project.User.Ldap.Config.Type."
431 "ActiveDirectory";
432 searchString = "active_directory";
433 }
434 else
435 {
436 BMCWEB_LOG_ERROR
437 << "Can't get the DbusType for the given type="
438 << ldapType;
439 callback(false, confData, ldapType);
440 return;
441 }
442
443 std::string ldapEnableInterfaceStr = ldapEnableInterface;
444 std::string ldapConfigInterfaceStr = ldapConfigInterface;
445
446 for (const auto& object : ldapObjects)
447 {
448 // let's find the object whose ldap type is equal to the
449 // given type
450 if (object.first.str.find(searchString) ==
451 std::string::npos)
452 {
453 continue;
454 }
455
456 for (const auto& interface : object.second)
457 {
458 if (interface.first == ldapEnableInterfaceStr)
459 {
460 // rest of the properties are string.
461 for (const auto& property : interface.second)
462 {
463 if (property.first == "Enabled")
464 {
465 const bool* value =
466 std::get_if<bool>(&property.second);
467 if (value == nullptr)
468 {
469 continue;
470 }
471 confData.serviceEnabled = *value;
472 break;
473 }
474 }
475 }
476 else if (interface.first == ldapConfigInterfaceStr)
477 {
478
479 for (const auto& property : interface.second)
480 {
Ed Tanous271584a2019-07-09 16:24:22 -0700481 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600482 std::get_if<std::string>(
483 &property.second);
Ed Tanous271584a2019-07-09 16:24:22 -0700484 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600485 {
486 continue;
487 }
488 if (property.first == "LDAPServerURI")
489 {
Ed Tanous271584a2019-07-09 16:24:22 -0700490 confData.uri = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600491 }
492 else if (property.first == "LDAPBindDN")
493 {
Ed Tanous271584a2019-07-09 16:24:22 -0700494 confData.bindDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600495 }
496 else if (property.first == "LDAPBaseDN")
497 {
Ed Tanous271584a2019-07-09 16:24:22 -0700498 confData.baseDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600499 }
500 else if (property.first ==
501 "LDAPSearchScope")
502 {
Ed Tanous271584a2019-07-09 16:24:22 -0700503 confData.searchScope = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600504 }
505 else if (property.first ==
506 "GroupNameAttribute")
507 {
Ed Tanous271584a2019-07-09 16:24:22 -0700508 confData.groupAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600509 }
510 else if (property.first ==
511 "UserNameAttribute")
512 {
Ed Tanous271584a2019-07-09 16:24:22 -0700513 confData.userNameAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600514 }
515 else if (property.first == "LDAPType")
516 {
Ed Tanous271584a2019-07-09 16:24:22 -0700517 confData.serverType = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600518 }
519 }
520 }
521 else if (interface.first ==
522 "xyz.openbmc_project.User."
523 "PrivilegeMapperEntry")
524 {
525 LDAPRoleMapData roleMapData{};
526 for (const auto& property : interface.second)
527 {
Ed Tanous271584a2019-07-09 16:24:22 -0700528 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600529 std::get_if<std::string>(
530 &property.second);
531
Ed Tanous271584a2019-07-09 16:24:22 -0700532 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600533 {
534 continue;
535 }
536
537 if (property.first == "GroupName")
538 {
Ed Tanous271584a2019-07-09 16:24:22 -0700539 roleMapData.groupName = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600540 }
541 else if (property.first == "Privilege")
542 {
Ed Tanous271584a2019-07-09 16:24:22 -0700543 roleMapData.privilege = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600544 }
545 }
546
Ed Tanous0f0353b2019-10-24 11:37:51 -0700547 confData.groupRoleList.emplace_back(
548 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600549 }
550 }
551 }
552 callback(true, confData, ldapType);
553 },
554 service, ldapRootObject, dbusObjManagerIntf,
555 "GetManagedObjects");
556 },
557 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
Ed Tanous23a21a12020-07-25 04:45:05 +0000558 ldapConfigObjectName, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530559}
560
Ed Tanous6c51eab2021-06-03 12:30:29 -0700561/**
562 * @brief parses the authentication section under the LDAP
563 * @param input JSON data
564 * @param asyncResp pointer to the JSON response
565 * @param userName userName to be filled from the given JSON.
566 * @param password password to be filled from the given JSON.
567 */
568void parseLDAPAuthenticationJson(
569 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
570 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700571{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700572 std::optional<std::string> authType;
573
574 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
575 authType, "Username", username, "Password",
576 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700578 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700579 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700580 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530581 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700582 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530583 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700584 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530585 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700586 messages::propertyValueNotInList(asyncResp->res, *authType,
587 "AuthenticationType");
588 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530589 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700590}
591/**
592 * @brief parses the LDAPService section under the LDAP
593 * @param input JSON data
594 * @param asyncResp pointer to the JSON response
595 * @param baseDNList baseDN to be filled from the given JSON.
596 * @param userNameAttribute userName to be filled from the given JSON.
597 * @param groupaAttribute password to be filled from the given JSON.
598 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530599
Ed Tanous6c51eab2021-06-03 12:30:29 -0700600void parseLDAPServiceJson(nlohmann::json input,
601 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
602 std::optional<std::vector<std::string>>& baseDNList,
603 std::optional<std::string>& userNameAttribute,
604 std::optional<std::string>& groupsAttribute)
605{
606 std::optional<nlohmann::json> searchSettings;
607
608 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
609 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530610 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700611 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530612 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700613 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530614 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700615 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530616 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700617 if (!json_util::readJson(*searchSettings, asyncResp->res,
618 "BaseDistinguishedNames", baseDNList,
619 "UsernameAttribute", userNameAttribute,
620 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530621 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700622 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530623 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700624}
625/**
626 * @brief updates the LDAP server address and updates the
627 json response with the new value.
628 * @param serviceAddressList address to be updated.
629 * @param asyncResp pointer to the JSON response
630 * @param ldapServerElementName Type of LDAP
631 server(openLDAP/ActiveDirectory)
632 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530633
Ed Tanous6c51eab2021-06-03 12:30:29 -0700634void handleServiceAddressPatch(
635 const std::vector<std::string>& serviceAddressList,
636 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
637 const std::string& ldapServerElementName,
638 const std::string& ldapConfigObject)
639{
640 crow::connections::systemBus->async_method_call(
641 [asyncResp, ldapServerElementName,
642 serviceAddressList](const boost::system::error_code ec) {
643 if (ec)
644 {
645 BMCWEB_LOG_DEBUG
646 << "Error Occurred in updating the service address";
647 messages::internalError(asyncResp->res);
648 return;
649 }
650 std::vector<std::string> modifiedserviceAddressList = {
651 serviceAddressList.front()};
652 asyncResp->res
653 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
654 modifiedserviceAddressList;
655 if ((serviceAddressList).size() > 1)
656 {
657 messages::propertyValueModified(asyncResp->res,
658 "ServiceAddresses",
659 serviceAddressList.front());
660 }
661 BMCWEB_LOG_DEBUG << "Updated the service address";
662 },
663 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
664 ldapConfigInterface, "LDAPServerURI",
665 std::variant<std::string>(serviceAddressList.front()));
666}
667/**
668 * @brief updates the LDAP Bind DN and updates the
669 json response with the new value.
670 * @param username name of the user which needs to be updated.
671 * @param asyncResp pointer to the JSON response
672 * @param ldapServerElementName Type of LDAP
673 server(openLDAP/ActiveDirectory)
674 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530675
Ed Tanous6c51eab2021-06-03 12:30:29 -0700676void handleUserNamePatch(const std::string& username,
zhanghch058d1b46d2021-04-01 11:18:24 +0800677 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous6c51eab2021-06-03 12:30:29 -0700678 const std::string& ldapServerElementName,
679 const std::string& ldapConfigObject)
680{
681 crow::connections::systemBus->async_method_call(
682 [asyncResp, username,
683 ldapServerElementName](const boost::system::error_code ec) {
684 if (ec)
685 {
686 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
687 messages::internalError(asyncResp->res);
688 return;
689 }
690 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
691 ["Username"] = username;
692 BMCWEB_LOG_DEBUG << "Updated the username";
693 },
694 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
695 ldapConfigInterface, "LDAPBindDN", std::variant<std::string>(username));
696}
697
698/**
699 * @brief updates the LDAP password
700 * @param password : ldap password which needs to be updated.
701 * @param asyncResp pointer to the JSON response
702 * @param ldapServerElementName Type of LDAP
703 * server(openLDAP/ActiveDirectory)
704 */
705
706void handlePasswordPatch(const std::string& password,
707 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
708 const std::string& ldapServerElementName,
709 const std::string& ldapConfigObject)
710{
711 crow::connections::systemBus->async_method_call(
712 [asyncResp, password,
713 ldapServerElementName](const boost::system::error_code ec) {
714 if (ec)
715 {
716 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
717 messages::internalError(asyncResp->res);
718 return;
719 }
720 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
721 ["Password"] = "";
722 BMCWEB_LOG_DEBUG << "Updated the password";
723 },
724 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
725 ldapConfigInterface, "LDAPBindDNPassword",
726 std::variant<std::string>(password));
727}
728
729/**
730 * @brief updates the LDAP BaseDN and updates the
731 json response with the new value.
732 * @param baseDNList baseDN list which needs to be updated.
733 * @param asyncResp pointer to the JSON response
734 * @param ldapServerElementName Type of LDAP
735 server(openLDAP/ActiveDirectory)
736 */
737
738void handleBaseDNPatch(const std::vector<std::string>& baseDNList,
739 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
740 const std::string& ldapServerElementName,
741 const std::string& ldapConfigObject)
742{
743 crow::connections::systemBus->async_method_call(
744 [asyncResp, baseDNList,
745 ldapServerElementName](const boost::system::error_code ec) {
746 if (ec)
747 {
748 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
749 messages::internalError(asyncResp->res);
750 return;
751 }
752 auto& serverTypeJson =
753 asyncResp->res.jsonValue[ldapServerElementName];
754 auto& searchSettingsJson =
755 serverTypeJson["LDAPService"]["SearchSettings"];
756 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
757 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
758 if (baseDNList.size() > 1)
759 {
760 messages::propertyValueModified(asyncResp->res,
761 "BaseDistinguishedNames",
762 baseDNList.front());
763 }
764 BMCWEB_LOG_DEBUG << "Updated the base DN";
765 },
766 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
767 ldapConfigInterface, "LDAPBaseDN",
768 std::variant<std::string>(baseDNList.front()));
769}
770/**
771 * @brief updates the LDAP user name attribute and updates the
772 json response with the new value.
773 * @param userNameAttribute attribute to be updated.
774 * @param asyncResp pointer to the JSON response
775 * @param ldapServerElementName Type of LDAP
776 server(openLDAP/ActiveDirectory)
777 */
778
779void handleUserNameAttrPatch(
780 const std::string& userNameAttribute,
781 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
782 const std::string& ldapServerElementName,
783 const std::string& ldapConfigObject)
784{
785 crow::connections::systemBus->async_method_call(
786 [asyncResp, userNameAttribute,
787 ldapServerElementName](const boost::system::error_code ec) {
788 if (ec)
789 {
790 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
791 "username attribute";
792 messages::internalError(asyncResp->res);
793 return;
794 }
795 auto& serverTypeJson =
796 asyncResp->res.jsonValue[ldapServerElementName];
797 auto& searchSettingsJson =
798 serverTypeJson["LDAPService"]["SearchSettings"];
799 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
800 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
801 },
802 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
803 ldapConfigInterface, "UserNameAttribute",
804 std::variant<std::string>(userNameAttribute));
805}
806/**
807 * @brief updates the LDAP group attribute and updates the
808 json response with the new value.
809 * @param groupsAttribute attribute to be updated.
810 * @param asyncResp pointer to the JSON response
811 * @param ldapServerElementName Type of LDAP
812 server(openLDAP/ActiveDirectory)
813 */
814
815void handleGroupNameAttrPatch(
816 const std::string& groupsAttribute,
817 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
818 const std::string& ldapServerElementName,
819 const std::string& ldapConfigObject)
820{
821 crow::connections::systemBus->async_method_call(
822 [asyncResp, groupsAttribute,
823 ldapServerElementName](const boost::system::error_code ec) {
824 if (ec)
825 {
826 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
827 "groupname attribute";
828 messages::internalError(asyncResp->res);
829 return;
830 }
831 auto& serverTypeJson =
832 asyncResp->res.jsonValue[ldapServerElementName];
833 auto& searchSettingsJson =
834 serverTypeJson["LDAPService"]["SearchSettings"];
835 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
836 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
837 },
838 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
839 ldapConfigInterface, "GroupNameAttribute",
840 std::variant<std::string>(groupsAttribute));
841}
842/**
843 * @brief updates the LDAP service enable and updates the
844 json response with the new value.
845 * @param input JSON data.
846 * @param asyncResp pointer to the JSON response
847 * @param ldapServerElementName Type of LDAP
848 server(openLDAP/ActiveDirectory)
849 */
850
851void handleServiceEnablePatch(
852 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
853 const std::string& ldapServerElementName,
854 const std::string& ldapConfigObject)
855{
856 crow::connections::systemBus->async_method_call(
857 [asyncResp, serviceEnabled,
858 ldapServerElementName](const boost::system::error_code ec) {
859 if (ec)
860 {
861 BMCWEB_LOG_DEBUG
862 << "Error Occurred in Updating the service enable";
863 messages::internalError(asyncResp->res);
864 return;
865 }
866 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
867 serviceEnabled;
868 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
869 },
870 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
871 ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
872}
873
874void handleAuthMethodsPatch(nlohmann::json& input,
875 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
876{
877 std::optional<bool> basicAuth;
878 std::optional<bool> cookie;
879 std::optional<bool> sessionToken;
880 std::optional<bool> xToken;
881 std::optional<bool> tls;
882
883 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
884 "Cookie", cookie, "SessionToken", sessionToken,
885 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530886 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700887 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
888 return;
889 }
890
891 // Make a copy of methods configuration
892 persistent_data::AuthConfigMethods authMethodsConfig =
893 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
894
895 if (basicAuth)
896 {
897#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
898 messages::actionNotSupported(
899 asyncResp->res, "Setting BasicAuth when basic-auth feature "
900 "is disabled");
901 return;
902#endif
903 authMethodsConfig.basic = *basicAuth;
904 }
905
906 if (cookie)
907 {
908#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
909 messages::actionNotSupported(asyncResp->res,
910 "Setting Cookie when cookie-auth feature "
911 "is disabled");
912 return;
913#endif
914 authMethodsConfig.cookie = *cookie;
915 }
916
917 if (sessionToken)
918 {
919#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
920 messages::actionNotSupported(
921 asyncResp->res, "Setting SessionToken when session-auth feature "
922 "is disabled");
923 return;
924#endif
925 authMethodsConfig.sessionToken = *sessionToken;
926 }
927
928 if (xToken)
929 {
930#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
931 messages::actionNotSupported(asyncResp->res,
932 "Setting XToken when xtoken-auth feature "
933 "is disabled");
934 return;
935#endif
936 authMethodsConfig.xtoken = *xToken;
937 }
938
939 if (tls)
940 {
941#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
942 messages::actionNotSupported(asyncResp->res,
943 "Setting TLS when mutual-tls-auth feature "
944 "is disabled");
945 return;
946#endif
947 authMethodsConfig.tls = *tls;
948 }
949
950 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
951 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
952 !authMethodsConfig.tls)
953 {
954 // Do not allow user to disable everything
955 messages::actionNotSupported(asyncResp->res,
956 "of disabling all available methods");
957 return;
958 }
959
960 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
961 authMethodsConfig);
962 // Save configuration immediately
963 persistent_data::getConfig().writeData();
964
965 messages::success(asyncResp->res);
966}
967
968/**
969 * @brief Get the required values from the given JSON, validates the
970 * value and create the LDAP config object.
971 * @param input JSON data
972 * @param asyncResp pointer to the JSON response
973 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
974 */
975
976inline void handleLDAPPatch(nlohmann::json& input,
977 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
978 const std::string& serverType)
979{
980 std::string dbusObjectPath;
981 if (serverType == "ActiveDirectory")
982 {
983 dbusObjectPath = adConfigObject;
984 }
985 else if (serverType == "LDAP")
986 {
987 dbusObjectPath = ldapConfigObjectName;
988 }
989 else
990 {
991 return;
992 }
993
994 std::optional<nlohmann::json> authentication;
995 std::optional<nlohmann::json> ldapService;
996 std::optional<std::vector<std::string>> serviceAddressList;
997 std::optional<bool> serviceEnabled;
998 std::optional<std::vector<std::string>> baseDNList;
999 std::optional<std::string> userNameAttribute;
1000 std::optional<std::string> groupsAttribute;
1001 std::optional<std::string> userName;
1002 std::optional<std::string> password;
1003 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1004
1005 if (!json_util::readJson(input, asyncResp->res, "Authentication",
1006 authentication, "LDAPService", ldapService,
1007 "ServiceAddresses", serviceAddressList,
1008 "ServiceEnabled", serviceEnabled,
1009 "RemoteRoleMapping", remoteRoleMapData))
1010 {
1011 return;
1012 }
1013
1014 if (authentication)
1015 {
1016 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1017 password);
1018 }
1019 if (ldapService)
1020 {
1021 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1022 userNameAttribute, groupsAttribute);
1023 }
1024 if (serviceAddressList)
1025 {
1026 if ((*serviceAddressList).size() == 0)
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301027 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001028 messages::propertyValueNotInList(asyncResp->res, "[]",
1029 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001030 return;
1031 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001032 }
1033 if (baseDNList)
1034 {
1035 if ((*baseDNList).size() == 0)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301036 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001037 messages::propertyValueNotInList(asyncResp->res, "[]",
1038 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301039 return;
1040 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001041 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301042
Ed Tanous6c51eab2021-06-03 12:30:29 -07001043 // nothing to update, then return
1044 if (!userName && !password && !serviceAddressList && !baseDNList &&
1045 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1046 !remoteRoleMapData)
1047 {
1048 return;
1049 }
1050
1051 // Get the existing resource first then keep modifying
1052 // whenever any property gets updated.
1053 getLDAPConfigData(serverType, [asyncResp, userName, password, baseDNList,
1054 userNameAttribute, groupsAttribute,
1055 serviceAddressList, serviceEnabled,
1056 dbusObjectPath, remoteRoleMapData](
1057 bool success,
1058 const LDAPConfigData& confData,
1059 const std::string& serverT) {
1060 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301061 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001062 messages::internalError(asyncResp->res);
1063 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301064 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001065 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1066 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301067 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001068 // Disable the service first and update the rest of
1069 // the properties.
1070 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301071 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001072
Ratan Gupta8a07d282019-03-16 08:33:47 +05301073 if (serviceAddressList)
1074 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001075 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1076 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301077 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001078 if (userName)
1079 {
1080 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1081 }
1082 if (password)
1083 {
1084 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1085 }
1086
Ratan Gupta8a07d282019-03-16 08:33:47 +05301087 if (baseDNList)
1088 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001089 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1090 }
1091 if (userNameAttribute)
1092 {
1093 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1094 dbusObjectPath);
1095 }
1096 if (groupsAttribute)
1097 {
1098 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1099 dbusObjectPath);
1100 }
1101 if (serviceEnabled)
1102 {
1103 // if user has given the value as true then enable
1104 // the service. if user has given false then no-op
1105 // as service is already stopped.
1106 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301107 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001108 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1109 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301110 }
1111 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001112 else
1113 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001114 // if user has not given the service enabled value
1115 // then revert it to the same state as it was
1116 // before.
1117 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1118 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001119 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001120
Ed Tanous6c51eab2021-06-03 12:30:29 -07001121 if (remoteRoleMapData)
1122 {
1123 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1124 *remoteRoleMapData);
1125 }
1126 });
1127}
1128
1129inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1130 const std::string& username,
1131 std::optional<std::string> password,
1132 std::optional<bool> enabled,
1133 std::optional<std::string> roleId,
1134 std::optional<bool> locked)
1135{
1136 std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
1137 dbus::utility::escapePathForDbus(dbusObjectPath);
1138
1139 dbus::utility::checkDbusPathExists(
1140 dbusObjectPath,
1141 [dbusObjectPath(std::move(dbusObjectPath)), username,
1142 password(std::move(password)), roleId(std::move(roleId)), enabled,
1143 locked, asyncResp{std::move(asyncResp)}](int rc) {
1144 if (!rc)
1145 {
1146 messages::resourceNotFound(
1147 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1148 username);
1149 return;
1150 }
1151
1152 if (password)
1153 {
1154 int retval = pamUpdatePassword(username, *password);
1155
1156 if (retval == PAM_USER_UNKNOWN)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001157 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001158 messages::resourceNotFound(
1159 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1160 username);
1161 }
1162 else if (retval == PAM_AUTHTOK_ERR)
1163 {
1164 // If password is invalid
1165 messages::propertyValueFormatError(asyncResp->res,
1166 *password, "Password");
1167 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1168 }
1169 else if (retval != PAM_SUCCESS)
1170 {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001171 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001172 return;
1173 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001174 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001175
Ed Tanous6c51eab2021-06-03 12:30:29 -07001176 if (enabled)
1177 {
1178 crow::connections::systemBus->async_method_call(
1179 [asyncResp](const boost::system::error_code ec) {
1180 if (ec)
1181 {
1182 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1183 messages::internalError(asyncResp->res);
1184 return;
1185 }
1186 messages::success(asyncResp->res);
1187 return;
1188 },
1189 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1190 "org.freedesktop.DBus.Properties", "Set",
1191 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1192 std::variant<bool>{*enabled});
1193 }
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001194
Ed Tanous6c51eab2021-06-03 12:30:29 -07001195 if (roleId)
1196 {
1197 std::string priv = getPrivilegeFromRoleId(*roleId);
1198 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001199 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001200 messages::propertyValueNotInList(asyncResp->res, *roleId,
1201 "RoleId");
1202 return;
1203 }
1204 if (priv == "priv-noaccess")
1205 {
1206 priv = "";
1207 }
1208
1209 crow::connections::systemBus->async_method_call(
1210 [asyncResp](const boost::system::error_code ec) {
1211 if (ec)
1212 {
1213 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1214 messages::internalError(asyncResp->res);
1215 return;
1216 }
1217 messages::success(asyncResp->res);
1218 },
1219 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1220 "org.freedesktop.DBus.Properties", "Set",
1221 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1222 std::variant<std::string>{priv});
1223 }
1224
1225 if (locked)
1226 {
1227 // admin can unlock the account which is locked by
1228 // successive authentication failures but admin should
1229 // not be allowed to lock an account.
1230 if (*locked)
1231 {
1232 messages::propertyValueNotInList(asyncResp->res, "true",
1233 "Locked");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001234 return;
1235 }
1236
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001237 crow::connections::systemBus->async_method_call(
Ed Tanous6c51eab2021-06-03 12:30:29 -07001238 [asyncResp](const boost::system::error_code ec) {
1239 if (ec)
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001240 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001241 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1242 messages::internalError(asyncResp->res);
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001243 return;
1244 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001245 messages::success(asyncResp->res);
1246 return;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001247 },
Ed Tanous6c51eab2021-06-03 12:30:29 -07001248 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1249 "org.freedesktop.DBus.Properties", "Set",
1250 "xyz.openbmc_project.User.Attributes",
1251 "UserLockedForFailedAttempt", std::variant<bool>{*locked});
1252 }
1253 });
1254}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001255
Ed Tanous6c51eab2021-06-03 12:30:29 -07001256inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001257{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001258
Ed Tanous6c51eab2021-06-03 12:30:29 -07001259 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
1260 .privileges({{"Login"}})
1261 .methods(
1262 boost::beast::http::verb::get)([](const crow::Request& /* req */,
1263 const std::shared_ptr<
1264 bmcweb::AsyncResp>& asyncResp)
1265 -> void {
1266 const persistent_data::AuthConfigMethods& authMethodsConfig =
1267 persistent_data::SessionStore::getInstance()
1268 .getAuthMethodsConfig();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001269
Ed Tanous6c51eab2021-06-03 12:30:29 -07001270 asyncResp->res.jsonValue = {
1271 {"@odata.id", "/redfish/v1/AccountService"},
1272 {"@odata.type", "#AccountService."
1273 "v1_5_0.AccountService"},
1274 {"Id", "AccountService"},
1275 {"Name", "Account Service"},
1276 {"Description", "Account Service"},
1277 {"ServiceEnabled", true},
1278 {"MaxPasswordLength", 20},
1279 {"Accounts",
1280 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
1281 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
1282 {"Oem",
1283 {{"OpenBMC",
1284 {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
1285 {"AuthMethods",
1286 {
1287 {"BasicAuth", authMethodsConfig.basic},
1288 {"SessionToken", authMethodsConfig.sessionToken},
1289 {"XToken", authMethodsConfig.xtoken},
1290 {"Cookie", authMethodsConfig.cookie},
1291 {"TLS", authMethodsConfig.tls},
1292 }}}}}},
1293 {"LDAP",
1294 {{"Certificates",
1295 {{"@odata.id",
1296 "/redfish/v1/AccountService/LDAP/Certificates"}}}}}};
1297 crow::connections::systemBus->async_method_call(
1298 [asyncResp](
1299 const boost::system::error_code ec,
1300 const std::vector<
1301 std::pair<std::string,
1302 std::variant<uint32_t, uint16_t, uint8_t>>>&
1303 propertiesList) {
1304 if (ec)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001305 {
1306 messages::internalError(asyncResp->res);
1307 return;
1308 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001309 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1310 << "properties for AccountService";
1311 for (const std::pair<std::string,
1312 std::variant<uint32_t, uint16_t,
1313 uint8_t>>& property :
1314 propertiesList)
1315 {
1316 if (property.first == "MinPasswordLength")
1317 {
1318 const uint8_t* value =
1319 std::get_if<uint8_t>(&property.second);
1320 if (value != nullptr)
Ratan Gupta24c85422019-01-30 19:41:24 +05301321 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001322 asyncResp->res.jsonValue["MinPasswordLength"] =
1323 *value;
Ratan Gupta24c85422019-01-30 19:41:24 +05301324 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001325 }
1326 if (property.first == "AccountUnlockTimeout")
1327 {
1328 const uint32_t* value =
1329 std::get_if<uint32_t>(&property.second);
1330 if (value != nullptr)
1331 {
1332 asyncResp->res
1333 .jsonValue["AccountLockoutDuration"] =
1334 *value;
1335 }
1336 }
1337 if (property.first == "MaxLoginAttemptBeforeLockout")
1338 {
1339 const uint16_t* value =
1340 std::get_if<uint16_t>(&property.second);
1341 if (value != nullptr)
1342 {
1343 asyncResp->res
1344 .jsonValue["AccountLockoutThreshold"] =
1345 *value;
1346 }
1347 }
1348 }
1349 },
1350 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1351 "org.freedesktop.DBus.Properties", "GetAll",
1352 "xyz.openbmc_project.User.AccountPolicy");
1353
1354 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
1355 const std::string& ldapType) {
1356 if (!success)
1357 {
1358 return;
1359 }
1360 parseLDAPConfigData(asyncResp->res.jsonValue, confData,
1361 ldapType);
1362 };
1363
1364 getLDAPConfigData("LDAP", callback);
1365 getLDAPConfigData("ActiveDirectory", callback);
1366 });
1367
1368 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
1369 // According to the PrivilegeRegistry, GET should actually be
1370 // "Login". A "Login" only privilege would return an empty "Members"
1371 // list. Not going to worry about this since none of the defined
1372 // roles are just "Login". E.g. Readonly is {"Login",
1373 // "ConfigureSelf"}. In the rare event anyone defines a role that
1374 // has Login but not ConfigureSelf, implement this.
1375 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1376 .methods(boost::beast::http::verb::get)(
1377 [](const crow::Request& req,
1378 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1379 asyncResp->res.jsonValue = {
1380 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
1381 {"@odata.type", "#ManagerAccountCollection."
1382 "ManagerAccountCollection"},
1383 {"Name", "Accounts Collection"},
1384 {"Description", "BMC User Accounts"}};
1385
1386 Privileges requiredPermissionsToSeeNonSelf = {
1387 {"ConfigureUsers"}};
1388 Privileges effectiveUserPrivileges =
1389 redfish::getUserPrivileges(req.userRole);
1390 bool userCanSeeAllAccounts =
1391 effectiveUserPrivileges.isSupersetOf(
1392 requiredPermissionsToSeeNonSelf);
1393
1394 std::string thisUser = req.session->username;
1395
1396 crow::connections::systemBus->async_method_call(
1397 [asyncResp, userCanSeeAllAccounts,
1398 thisUser](const boost::system::error_code ec,
1399 const ManagedObjectType& users) {
1400 if (ec)
1401 {
1402 messages::internalError(asyncResp->res);
Ratan Gupta24c85422019-01-30 19:41:24 +05301403 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001404 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001405
Ed Tanous6c51eab2021-06-03 12:30:29 -07001406 nlohmann::json& memberArray =
1407 asyncResp->res.jsonValue["Members"];
1408 memberArray = nlohmann::json::array();
Ratan Gupta24c85422019-01-30 19:41:24 +05301409
Ed Tanous6c51eab2021-06-03 12:30:29 -07001410 for (auto& userpath : users)
1411 {
1412 std::string user = userpath.first.filename();
1413 if (user.empty())
Ratan Gupta24c85422019-01-30 19:41:24 +05301414 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301415 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001416 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1417
Ratan Gupta24c85422019-01-30 19:41:24 +05301418 return;
1419 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301420
Ed Tanous6c51eab2021-06-03 12:30:29 -07001421 // As clarified by Redfish here:
1422 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1423 // Users without ConfigureUsers, only see their own
1424 // account. Users with ConfigureUsers, see all
1425 // accounts.
1426 if (thisUser == user || userCanSeeAllAccounts)
Ratan Gupta24c85422019-01-30 19:41:24 +05301427 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001428 memberArray.push_back(
1429 {{"@odata.id",
1430 "/redfish/v1/AccountService/Accounts/" +
1431 user}});
Ratan Gupta24c85422019-01-30 19:41:24 +05301432 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001433 }
1434 asyncResp->res.jsonValue["Members@odata.count"] =
1435 memberArray.size();
1436 },
1437 "xyz.openbmc_project.User.Manager",
1438 "/xyz/openbmc_project/user",
1439 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Ratan Gupta24c85422019-01-30 19:41:24 +05301440 });
Ed Tanous06e086d2018-09-19 17:19:52 -07001441
Ed Tanous6c51eab2021-06-03 12:30:29 -07001442 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
1443 .privileges({{"ConfigureUsers"}})
1444 .methods(boost::beast::http::verb::post)([](const crow::Request& req,
1445 const std::shared_ptr<
1446 bmcweb::AsyncResp>&
1447 asyncResp) -> void {
1448 std::string username;
1449 std::string password;
1450 std::optional<std::string> roleId("User");
1451 std::optional<bool> enabled = true;
1452 if (!json_util::readJson(req, asyncResp->res, "UserName", username,
1453 "Password", password, "RoleId", roleId,
1454 "Enabled", enabled))
1455 {
1456 return;
1457 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001458
Ed Tanous6c51eab2021-06-03 12:30:29 -07001459 std::string priv = getPrivilegeFromRoleId(*roleId);
1460 if (priv.empty())
1461 {
1462 messages::propertyValueNotInList(asyncResp->res, *roleId,
1463 "RoleId");
1464 return;
1465 }
1466 // TODO: Following override will be reverted once support in
1467 // phosphor-user-manager is added. In order to avoid dependency
1468 // issues, this is added in bmcweb, which will removed, once
1469 // phosphor-user-manager supports priv-noaccess.
1470 if (priv == "priv-noaccess")
1471 {
1472 roleId = "";
1473 }
1474 else
1475 {
1476 roleId = priv;
1477 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001478
Ed Tanous6c51eab2021-06-03 12:30:29 -07001479 // Reading AllGroups property
1480 crow::connections::systemBus->async_method_call(
1481 [asyncResp, username, password{std::move(password)}, roleId,
1482 enabled](
1483 const boost::system::error_code ec,
1484 const std::variant<std::vector<std::string>>& allGroups) {
1485 if (ec)
1486 {
1487 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1488 messages::internalError(asyncResp->res);
1489 return;
1490 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001491
Ed Tanous6c51eab2021-06-03 12:30:29 -07001492 const std::vector<std::string>* allGroupsList =
1493 std::get_if<std::vector<std::string>>(&allGroups);
1494
1495 if (allGroupsList == nullptr || allGroupsList->empty())
1496 {
1497 messages::internalError(asyncResp->res);
1498 return;
1499 }
1500
1501 crow::connections::systemBus->async_method_call(
1502 [asyncResp, username,
1503 password](const boost::system::error_code ec2,
1504 sdbusplus::message::message& m) {
1505 if (ec2)
1506 {
1507 userErrorMessageHandler(
1508 m.get_error(), asyncResp, username, "");
1509 return;
1510 }
1511
1512 if (pamUpdatePassword(username, password) !=
1513 PAM_SUCCESS)
1514 {
1515 // At this point we have a user that's been
1516 // created, but the password set
1517 // failed.Something is wrong, so delete the user
1518 // that we've already created
1519 crow::connections::systemBus->async_method_call(
1520 [asyncResp, password](
1521 const boost::system::error_code ec3) {
1522 if (ec3)
1523 {
1524 messages::internalError(
1525 asyncResp->res);
1526 return;
1527 }
1528
1529 // If password is invalid
1530 messages::propertyValueFormatError(
1531 asyncResp->res, password,
1532 "Password");
1533 },
1534 "xyz.openbmc_project.User.Manager",
1535 "/xyz/openbmc_project/user/" + username,
1536 "xyz.openbmc_project.Object.Delete",
1537 "Delete");
1538
1539 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1540 return;
1541 }
1542
1543 messages::created(asyncResp->res);
1544 asyncResp->res.addHeader(
1545 "Location",
1546 "/redfish/v1/AccountService/Accounts/" +
1547 username);
1548 },
1549 "xyz.openbmc_project.User.Manager",
1550 "/xyz/openbmc_project/user",
1551 "xyz.openbmc_project.User.Manager", "CreateUser",
1552 username, *allGroupsList, *roleId, *enabled);
1553 },
1554 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1555 "org.freedesktop.DBus.Properties", "Get",
1556 "xyz.openbmc_project.User.Manager", "AllGroups");
1557 });
1558
1559 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
1560 .privileges(
1561 {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}})
1562 .methods(
1563 boost::beast::http::verb::
1564 get)([](const crow::Request& req,
1565 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1566 const std::string& accountName) -> void {
1567 if (req.session->username != accountName)
1568 {
1569 // At this point we've determined that the user is trying to
1570 // modify a user that isn't them. We need to verify that they
1571 // have permissions to modify other users, so re-run the auth
1572 // check with the same permissions, minus ConfigureSelf.
1573 Privileges effectiveUserPrivileges =
1574 redfish::getUserPrivileges(req.userRole);
1575 Privileges requiredPermissionsToChangeNonSelf = {
1576 {"ConfigureUsers"}, {"ConfigureManager"}};
1577 if (!effectiveUserPrivileges.isSupersetOf(
1578 requiredPermissionsToChangeNonSelf))
Ed Tanous06e086d2018-09-19 17:19:52 -07001579 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001580 BMCWEB_LOG_DEBUG << "GET Account denied access";
1581 messages::insufficientPrivilege(asyncResp->res);
1582 return;
1583 }
1584 }
1585
1586 crow::connections::systemBus->async_method_call(
1587 [asyncResp, accountName](const boost::system::error_code ec,
1588 const ManagedObjectType& users) {
1589 if (ec)
1590 {
1591 messages::internalError(asyncResp->res);
1592 return;
1593 }
1594 auto userIt = users.begin();
1595
1596 for (; userIt != users.end(); userIt++)
1597 {
1598 if (boost::ends_with(userIt->first.str,
1599 "/" + accountName))
1600 {
1601 break;
1602 }
1603 }
1604 if (userIt == users.end())
1605 {
1606 messages::resourceNotFound(
1607 asyncResp->res, "ManagerAccount", accountName);
1608 return;
1609 }
1610
1611 asyncResp->res.jsonValue = {
1612 {"@odata.type",
1613 "#ManagerAccount.v1_4_0.ManagerAccount"},
1614 {"Name", "User Account"},
1615 {"Description", "User Account"},
1616 {"Password", nullptr},
1617 {"AccountTypes", {"Redfish"}}};
1618
1619 for (const auto& interface : userIt->second)
1620 {
1621 if (interface.first ==
1622 "xyz.openbmc_project.User.Attributes")
1623 {
1624 for (const auto& property : interface.second)
1625 {
1626 if (property.first == "UserEnabled")
1627 {
1628 const bool* userEnabled =
1629 std::get_if<bool>(&property.second);
1630 if (userEnabled == nullptr)
1631 {
1632 BMCWEB_LOG_ERROR
1633 << "UserEnabled wasn't a bool";
1634 messages::internalError(asyncResp->res);
1635 return;
1636 }
1637 asyncResp->res.jsonValue["Enabled"] =
1638 *userEnabled;
1639 }
1640 else if (property.first ==
1641 "UserLockedForFailedAttempt")
1642 {
1643 const bool* userLocked =
1644 std::get_if<bool>(&property.second);
1645 if (userLocked == nullptr)
1646 {
1647 BMCWEB_LOG_ERROR << "UserLockedForF"
1648 "ailedAttempt "
1649 "wasn't a bool";
1650 messages::internalError(asyncResp->res);
1651 return;
1652 }
1653 asyncResp->res.jsonValue["Locked"] =
1654 *userLocked;
1655 asyncResp->res.jsonValue
1656 ["Locked@Redfish.AllowableValues"] = {
1657 "false"}; // can only unlock accounts
1658 }
1659 else if (property.first == "UserPrivilege")
1660 {
1661 const std::string* userPrivPtr =
1662 std::get_if<std::string>(
1663 &property.second);
1664 if (userPrivPtr == nullptr)
1665 {
1666 BMCWEB_LOG_ERROR
1667 << "UserPrivilege wasn't a "
1668 "string";
1669 messages::internalError(asyncResp->res);
1670 return;
1671 }
1672 std::string role =
1673 getRoleIdFromPrivilege(*userPrivPtr);
1674 if (role.empty())
1675 {
1676 BMCWEB_LOG_ERROR << "Invalid user role";
1677 messages::internalError(asyncResp->res);
1678 return;
1679 }
1680 asyncResp->res.jsonValue["RoleId"] = role;
1681
1682 asyncResp->res.jsonValue["Links"]["Role"] =
1683 {{"@odata.id",
1684 "/redfish/v1/AccountService/"
1685 "Roles/" +
1686 role}};
1687 }
1688 else if (property.first ==
1689 "UserPasswordExpired")
1690 {
1691 const bool* userPasswordExpired =
1692 std::get_if<bool>(&property.second);
1693 if (userPasswordExpired == nullptr)
1694 {
1695 BMCWEB_LOG_ERROR << "UserPassword"
1696 "Expired "
1697 "wasn't a bool";
1698 messages::internalError(asyncResp->res);
1699 return;
1700 }
1701 asyncResp->res
1702 .jsonValue["PasswordChangeRequired"] =
1703 *userPasswordExpired;
1704 }
1705 }
1706 }
1707 }
1708
1709 asyncResp->res.jsonValue["@odata.id"] =
1710 "/redfish/v1/AccountService/Accounts/" + accountName;
1711 asyncResp->res.jsonValue["Id"] = accountName;
1712 asyncResp->res.jsonValue["UserName"] = accountName;
1713 },
1714 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1715 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1716 });
1717
1718 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
1719 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1720 .methods(boost::beast::http::verb::patch)(
1721 [](const crow::Request& req,
1722 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1723 const std::string& username) -> void {
1724 std::optional<std::string> newUserName;
1725 std::optional<std::string> password;
1726 std::optional<bool> enabled;
1727 std::optional<std::string> roleId;
1728 std::optional<bool> locked;
1729 if (!json_util::readJson(req, asyncResp->res, "UserName",
1730 newUserName, "Password", password,
1731 "RoleId", roleId, "Enabled", enabled,
1732 "Locked", locked))
1733 {
Ed Tanous06e086d2018-09-19 17:19:52 -07001734 return;
1735 }
1736
Ed Tanous6c51eab2021-06-03 12:30:29 -07001737 // Perform a proper ConfigureSelf authority check. If the
1738 // session is being used to PATCH a property other than
1739 // Password, then the ConfigureSelf privilege does not apply.
1740 // If the user is operating on an account not their own, then
1741 // their ConfigureSelf privilege does not apply. In either
1742 // case, perform the authority check again without the user's
1743 // ConfigureSelf privilege.
1744 if ((username != req.session->username))
1745 {
1746 Privileges requiredPermissionsToChangeNonSelf = {
1747 {"ConfigureUsers"}};
1748 Privileges effectiveUserPrivileges =
1749 redfish::getUserPrivileges(req.userRole);
1750
1751 if (!effectiveUserPrivileges.isSupersetOf(
1752 requiredPermissionsToChangeNonSelf))
1753 {
1754 messages::insufficientPrivilege(asyncResp->res);
1755 return;
1756 }
1757 }
1758
1759 // if user name is not provided in the patch method or if it
1760 // matches the user name in the URI, then we are treating it as
1761 // updating user properties other then username. If username
1762 // provided doesn't match the URI, then we are treating this as
1763 // user rename request.
1764 if (!newUserName || (newUserName.value() == username))
1765 {
1766 updateUserProperties(asyncResp, username, password, enabled,
1767 roleId, locked);
1768 return;
1769 }
1770 crow::connections::systemBus->async_method_call(
1771 [asyncResp, username, password(std::move(password)),
1772 roleId(std::move(roleId)), enabled,
1773 newUser{std::string(*newUserName)},
1774 locked](const boost::system::error_code ec,
1775 sdbusplus::message::message& m) {
1776 if (ec)
1777 {
1778 userErrorMessageHandler(m.get_error(), asyncResp,
1779 newUser, username);
1780 return;
1781 }
1782
1783 updateUserProperties(asyncResp, newUser, password,
1784 enabled, roleId, locked);
1785 },
1786 "xyz.openbmc_project.User.Manager",
1787 "/xyz/openbmc_project/user",
1788 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1789 *newUserName);
1790 });
1791
1792 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
1793 .privileges({{"ConfigureUsers"}})
1794 .methods(boost::beast::http::verb::delete_)(
1795 [](const crow::Request& /*req*/,
1796 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1797 const std::string& username) -> void {
1798 const std::string userPath =
1799 "/xyz/openbmc_project/user/" + username;
1800
1801 crow::connections::systemBus->async_method_call(
1802 [asyncResp, username](const boost::system::error_code ec) {
1803 if (ec)
1804 {
1805 messages::resourceNotFound(
1806 asyncResp->res,
1807 "#ManagerAccount.v1_4_0.ManagerAccount",
1808 username);
1809 return;
1810 }
1811
1812 messages::accountRemoved(asyncResp->res);
1813 },
1814 "xyz.openbmc_project.User.Manager", userPath,
1815 "xyz.openbmc_project.Object.Delete", "Delete");
1816 });
1817}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001818
Ed Tanous1abe55e2018-09-05 08:30:59 -07001819} // namespace redfish