blob: 8e27930304fe9eeedaa359176c0d3c1817f03f8c [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 Tanoused398212021-06-09 17:05:54 -070023#include <registries/privilege_registry.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 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700568inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700569 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 Tanous4f48d5f2021-06-21 08:27:45 -0700600inline void
601 parseLDAPServiceJson(nlohmann::json input,
602 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
603 std::optional<std::vector<std::string>>& baseDNList,
604 std::optional<std::string>& userNameAttribute,
605 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700606{
607 std::optional<nlohmann::json> searchSettings;
608
609 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
610 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530611 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700612 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530613 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700614 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530615 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700616 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530617 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700618 if (!json_util::readJson(*searchSettings, asyncResp->res,
619 "BaseDistinguishedNames", baseDNList,
620 "UsernameAttribute", userNameAttribute,
621 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530622 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700623 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530624 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700625}
626/**
627 * @brief updates the LDAP server address and updates the
628 json response with the new value.
629 * @param serviceAddressList address to be updated.
630 * @param asyncResp pointer to the JSON response
631 * @param ldapServerElementName Type of LDAP
632 server(openLDAP/ActiveDirectory)
633 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530634
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700635inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700636 const std::vector<std::string>& serviceAddressList,
637 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
638 const std::string& ldapServerElementName,
639 const std::string& ldapConfigObject)
640{
641 crow::connections::systemBus->async_method_call(
642 [asyncResp, ldapServerElementName,
643 serviceAddressList](const boost::system::error_code ec) {
644 if (ec)
645 {
646 BMCWEB_LOG_DEBUG
647 << "Error Occurred in updating the service address";
648 messages::internalError(asyncResp->res);
649 return;
650 }
651 std::vector<std::string> modifiedserviceAddressList = {
652 serviceAddressList.front()};
653 asyncResp->res
654 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
655 modifiedserviceAddressList;
656 if ((serviceAddressList).size() > 1)
657 {
658 messages::propertyValueModified(asyncResp->res,
659 "ServiceAddresses",
660 serviceAddressList.front());
661 }
662 BMCWEB_LOG_DEBUG << "Updated the service address";
663 },
664 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
665 ldapConfigInterface, "LDAPServerURI",
666 std::variant<std::string>(serviceAddressList.front()));
667}
668/**
669 * @brief updates the LDAP Bind DN and updates the
670 json response with the new value.
671 * @param username name of the user which needs to be updated.
672 * @param asyncResp pointer to the JSON response
673 * @param ldapServerElementName Type of LDAP
674 server(openLDAP/ActiveDirectory)
675 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530676
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700677inline void
678 handleUserNamePatch(const std::string& username,
679 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
680 const std::string& ldapServerElementName,
681 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700682{
683 crow::connections::systemBus->async_method_call(
684 [asyncResp, username,
685 ldapServerElementName](const boost::system::error_code ec) {
686 if (ec)
687 {
688 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
689 messages::internalError(asyncResp->res);
690 return;
691 }
692 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
693 ["Username"] = username;
694 BMCWEB_LOG_DEBUG << "Updated the username";
695 },
696 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
697 ldapConfigInterface, "LDAPBindDN", std::variant<std::string>(username));
698}
699
700/**
701 * @brief updates the LDAP password
702 * @param password : ldap password which needs to be updated.
703 * @param asyncResp pointer to the JSON response
704 * @param ldapServerElementName Type of LDAP
705 * server(openLDAP/ActiveDirectory)
706 */
707
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700708inline void
709 handlePasswordPatch(const std::string& password,
710 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
711 const std::string& ldapServerElementName,
712 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700713{
714 crow::connections::systemBus->async_method_call(
715 [asyncResp, password,
716 ldapServerElementName](const boost::system::error_code ec) {
717 if (ec)
718 {
719 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
720 messages::internalError(asyncResp->res);
721 return;
722 }
723 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
724 ["Password"] = "";
725 BMCWEB_LOG_DEBUG << "Updated the password";
726 },
727 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
728 ldapConfigInterface, "LDAPBindDNPassword",
729 std::variant<std::string>(password));
730}
731
732/**
733 * @brief updates the LDAP BaseDN and updates the
734 json response with the new value.
735 * @param baseDNList baseDN list which needs to be updated.
736 * @param asyncResp pointer to the JSON response
737 * @param ldapServerElementName Type of LDAP
738 server(openLDAP/ActiveDirectory)
739 */
740
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700741inline void
742 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
743 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
744 const std::string& ldapServerElementName,
745 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700746{
747 crow::connections::systemBus->async_method_call(
748 [asyncResp, baseDNList,
749 ldapServerElementName](const boost::system::error_code ec) {
750 if (ec)
751 {
752 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
753 messages::internalError(asyncResp->res);
754 return;
755 }
756 auto& serverTypeJson =
757 asyncResp->res.jsonValue[ldapServerElementName];
758 auto& searchSettingsJson =
759 serverTypeJson["LDAPService"]["SearchSettings"];
760 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
761 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
762 if (baseDNList.size() > 1)
763 {
764 messages::propertyValueModified(asyncResp->res,
765 "BaseDistinguishedNames",
766 baseDNList.front());
767 }
768 BMCWEB_LOG_DEBUG << "Updated the base DN";
769 },
770 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
771 ldapConfigInterface, "LDAPBaseDN",
772 std::variant<std::string>(baseDNList.front()));
773}
774/**
775 * @brief updates the LDAP user name attribute and updates the
776 json response with the new value.
777 * @param userNameAttribute attribute to be updated.
778 * @param asyncResp pointer to the JSON response
779 * @param ldapServerElementName Type of LDAP
780 server(openLDAP/ActiveDirectory)
781 */
782
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700783inline void
784 handleUserNameAttrPatch(const std::string& userNameAttribute,
785 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
786 const std::string& ldapServerElementName,
787 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700788{
789 crow::connections::systemBus->async_method_call(
790 [asyncResp, userNameAttribute,
791 ldapServerElementName](const boost::system::error_code ec) {
792 if (ec)
793 {
794 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
795 "username attribute";
796 messages::internalError(asyncResp->res);
797 return;
798 }
799 auto& serverTypeJson =
800 asyncResp->res.jsonValue[ldapServerElementName];
801 auto& searchSettingsJson =
802 serverTypeJson["LDAPService"]["SearchSettings"];
803 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
804 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
805 },
806 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
807 ldapConfigInterface, "UserNameAttribute",
808 std::variant<std::string>(userNameAttribute));
809}
810/**
811 * @brief updates the LDAP group attribute and updates the
812 json response with the new value.
813 * @param groupsAttribute attribute to be updated.
814 * @param asyncResp pointer to the JSON response
815 * @param ldapServerElementName Type of LDAP
816 server(openLDAP/ActiveDirectory)
817 */
818
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700819inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700820 const std::string& groupsAttribute,
821 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
822 const std::string& ldapServerElementName,
823 const std::string& ldapConfigObject)
824{
825 crow::connections::systemBus->async_method_call(
826 [asyncResp, groupsAttribute,
827 ldapServerElementName](const boost::system::error_code ec) {
828 if (ec)
829 {
830 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
831 "groupname attribute";
832 messages::internalError(asyncResp->res);
833 return;
834 }
835 auto& serverTypeJson =
836 asyncResp->res.jsonValue[ldapServerElementName];
837 auto& searchSettingsJson =
838 serverTypeJson["LDAPService"]["SearchSettings"];
839 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
840 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
841 },
842 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
843 ldapConfigInterface, "GroupNameAttribute",
844 std::variant<std::string>(groupsAttribute));
845}
846/**
847 * @brief updates the LDAP service enable and updates the
848 json response with the new value.
849 * @param input JSON data.
850 * @param asyncResp pointer to the JSON response
851 * @param ldapServerElementName Type of LDAP
852 server(openLDAP/ActiveDirectory)
853 */
854
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700855inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700856 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
857 const std::string& ldapServerElementName,
858 const std::string& ldapConfigObject)
859{
860 crow::connections::systemBus->async_method_call(
861 [asyncResp, serviceEnabled,
862 ldapServerElementName](const boost::system::error_code ec) {
863 if (ec)
864 {
865 BMCWEB_LOG_DEBUG
866 << "Error Occurred in Updating the service enable";
867 messages::internalError(asyncResp->res);
868 return;
869 }
870 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
871 serviceEnabled;
872 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
873 },
874 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
875 ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
876}
877
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700878inline void
879 handleAuthMethodsPatch(nlohmann::json& input,
880 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700881{
882 std::optional<bool> basicAuth;
883 std::optional<bool> cookie;
884 std::optional<bool> sessionToken;
885 std::optional<bool> xToken;
886 std::optional<bool> tls;
887
888 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
889 "Cookie", cookie, "SessionToken", sessionToken,
890 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530891 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700892 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
893 return;
894 }
895
896 // Make a copy of methods configuration
897 persistent_data::AuthConfigMethods authMethodsConfig =
898 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
899
900 if (basicAuth)
901 {
902#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
903 messages::actionNotSupported(
904 asyncResp->res, "Setting BasicAuth when basic-auth feature "
905 "is disabled");
906 return;
907#endif
908 authMethodsConfig.basic = *basicAuth;
909 }
910
911 if (cookie)
912 {
913#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
914 messages::actionNotSupported(asyncResp->res,
915 "Setting Cookie when cookie-auth feature "
916 "is disabled");
917 return;
918#endif
919 authMethodsConfig.cookie = *cookie;
920 }
921
922 if (sessionToken)
923 {
924#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
925 messages::actionNotSupported(
926 asyncResp->res, "Setting SessionToken when session-auth feature "
927 "is disabled");
928 return;
929#endif
930 authMethodsConfig.sessionToken = *sessionToken;
931 }
932
933 if (xToken)
934 {
935#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
936 messages::actionNotSupported(asyncResp->res,
937 "Setting XToken when xtoken-auth feature "
938 "is disabled");
939 return;
940#endif
941 authMethodsConfig.xtoken = *xToken;
942 }
943
944 if (tls)
945 {
946#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
947 messages::actionNotSupported(asyncResp->res,
948 "Setting TLS when mutual-tls-auth feature "
949 "is disabled");
950 return;
951#endif
952 authMethodsConfig.tls = *tls;
953 }
954
955 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
956 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
957 !authMethodsConfig.tls)
958 {
959 // Do not allow user to disable everything
960 messages::actionNotSupported(asyncResp->res,
961 "of disabling all available methods");
962 return;
963 }
964
965 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
966 authMethodsConfig);
967 // Save configuration immediately
968 persistent_data::getConfig().writeData();
969
970 messages::success(asyncResp->res);
971}
972
973/**
974 * @brief Get the required values from the given JSON, validates the
975 * value and create the LDAP config object.
976 * @param input JSON data
977 * @param asyncResp pointer to the JSON response
978 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
979 */
980
981inline void handleLDAPPatch(nlohmann::json& input,
982 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
983 const std::string& serverType)
984{
985 std::string dbusObjectPath;
986 if (serverType == "ActiveDirectory")
987 {
988 dbusObjectPath = adConfigObject;
989 }
990 else if (serverType == "LDAP")
991 {
992 dbusObjectPath = ldapConfigObjectName;
993 }
994 else
995 {
996 return;
997 }
998
999 std::optional<nlohmann::json> authentication;
1000 std::optional<nlohmann::json> ldapService;
1001 std::optional<std::vector<std::string>> serviceAddressList;
1002 std::optional<bool> serviceEnabled;
1003 std::optional<std::vector<std::string>> baseDNList;
1004 std::optional<std::string> userNameAttribute;
1005 std::optional<std::string> groupsAttribute;
1006 std::optional<std::string> userName;
1007 std::optional<std::string> password;
1008 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1009
1010 if (!json_util::readJson(input, asyncResp->res, "Authentication",
1011 authentication, "LDAPService", ldapService,
1012 "ServiceAddresses", serviceAddressList,
1013 "ServiceEnabled", serviceEnabled,
1014 "RemoteRoleMapping", remoteRoleMapData))
1015 {
1016 return;
1017 }
1018
1019 if (authentication)
1020 {
1021 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1022 password);
1023 }
1024 if (ldapService)
1025 {
1026 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1027 userNameAttribute, groupsAttribute);
1028 }
1029 if (serviceAddressList)
1030 {
1031 if ((*serviceAddressList).size() == 0)
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301032 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001033 messages::propertyValueNotInList(asyncResp->res, "[]",
1034 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001035 return;
1036 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001037 }
1038 if (baseDNList)
1039 {
1040 if ((*baseDNList).size() == 0)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301041 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001042 messages::propertyValueNotInList(asyncResp->res, "[]",
1043 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301044 return;
1045 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001046 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301047
Ed Tanous6c51eab2021-06-03 12:30:29 -07001048 // nothing to update, then return
1049 if (!userName && !password && !serviceAddressList && !baseDNList &&
1050 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1051 !remoteRoleMapData)
1052 {
1053 return;
1054 }
1055
1056 // Get the existing resource first then keep modifying
1057 // whenever any property gets updated.
1058 getLDAPConfigData(serverType, [asyncResp, userName, password, baseDNList,
1059 userNameAttribute, groupsAttribute,
1060 serviceAddressList, serviceEnabled,
1061 dbusObjectPath, remoteRoleMapData](
1062 bool success,
1063 const LDAPConfigData& confData,
1064 const std::string& serverT) {
1065 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301066 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001067 messages::internalError(asyncResp->res);
1068 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301069 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001070 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1071 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301072 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001073 // Disable the service first and update the rest of
1074 // the properties.
1075 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301076 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001077
Ratan Gupta8a07d282019-03-16 08:33:47 +05301078 if (serviceAddressList)
1079 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001080 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1081 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301082 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001083 if (userName)
1084 {
1085 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1086 }
1087 if (password)
1088 {
1089 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1090 }
1091
Ratan Gupta8a07d282019-03-16 08:33:47 +05301092 if (baseDNList)
1093 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001094 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1095 }
1096 if (userNameAttribute)
1097 {
1098 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1099 dbusObjectPath);
1100 }
1101 if (groupsAttribute)
1102 {
1103 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1104 dbusObjectPath);
1105 }
1106 if (serviceEnabled)
1107 {
1108 // if user has given the value as true then enable
1109 // the service. if user has given false then no-op
1110 // as service is already stopped.
1111 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301112 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001113 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1114 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301115 }
1116 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001117 else
1118 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001119 // if user has not given the service enabled value
1120 // then revert it to the same state as it was
1121 // before.
1122 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1123 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001124 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001125
Ed Tanous6c51eab2021-06-03 12:30:29 -07001126 if (remoteRoleMapData)
1127 {
1128 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1129 *remoteRoleMapData);
1130 }
1131 });
1132}
1133
1134inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1135 const std::string& username,
1136 std::optional<std::string> password,
1137 std::optional<bool> enabled,
1138 std::optional<std::string> roleId,
1139 std::optional<bool> locked)
1140{
1141 std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
1142 dbus::utility::escapePathForDbus(dbusObjectPath);
1143
1144 dbus::utility::checkDbusPathExists(
1145 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001146 [dbusObjectPath, username, password(std::move(password)),
1147 roleId(std::move(roleId)), enabled, locked,
1148 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001149 if (!rc)
1150 {
1151 messages::resourceNotFound(
1152 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1153 username);
1154 return;
1155 }
1156
1157 if (password)
1158 {
1159 int retval = pamUpdatePassword(username, *password);
1160
1161 if (retval == PAM_USER_UNKNOWN)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001162 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001163 messages::resourceNotFound(
1164 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1165 username);
1166 }
1167 else if (retval == PAM_AUTHTOK_ERR)
1168 {
1169 // If password is invalid
1170 messages::propertyValueFormatError(asyncResp->res,
1171 *password, "Password");
1172 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1173 }
1174 else if (retval != PAM_SUCCESS)
1175 {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001176 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001177 return;
1178 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001179 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001180
Ed Tanous6c51eab2021-06-03 12:30:29 -07001181 if (enabled)
1182 {
1183 crow::connections::systemBus->async_method_call(
1184 [asyncResp](const boost::system::error_code ec) {
1185 if (ec)
1186 {
1187 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1188 messages::internalError(asyncResp->res);
1189 return;
1190 }
1191 messages::success(asyncResp->res);
1192 return;
1193 },
1194 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1195 "org.freedesktop.DBus.Properties", "Set",
1196 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1197 std::variant<bool>{*enabled});
1198 }
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001199
Ed Tanous6c51eab2021-06-03 12:30:29 -07001200 if (roleId)
1201 {
1202 std::string priv = getPrivilegeFromRoleId(*roleId);
1203 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001204 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001205 messages::propertyValueNotInList(asyncResp->res, *roleId,
1206 "RoleId");
1207 return;
1208 }
1209 if (priv == "priv-noaccess")
1210 {
1211 priv = "";
1212 }
1213
1214 crow::connections::systemBus->async_method_call(
1215 [asyncResp](const boost::system::error_code ec) {
1216 if (ec)
1217 {
1218 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1219 messages::internalError(asyncResp->res);
1220 return;
1221 }
1222 messages::success(asyncResp->res);
1223 },
1224 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1225 "org.freedesktop.DBus.Properties", "Set",
1226 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1227 std::variant<std::string>{priv});
1228 }
1229
1230 if (locked)
1231 {
1232 // admin can unlock the account which is locked by
1233 // successive authentication failures but admin should
1234 // not be allowed to lock an account.
1235 if (*locked)
1236 {
1237 messages::propertyValueNotInList(asyncResp->res, "true",
1238 "Locked");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001239 return;
1240 }
1241
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001242 crow::connections::systemBus->async_method_call(
Ed Tanous6c51eab2021-06-03 12:30:29 -07001243 [asyncResp](const boost::system::error_code ec) {
1244 if (ec)
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001245 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001246 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1247 messages::internalError(asyncResp->res);
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001248 return;
1249 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001250 messages::success(asyncResp->res);
1251 return;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001252 },
Ed Tanous6c51eab2021-06-03 12:30:29 -07001253 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1254 "org.freedesktop.DBus.Properties", "Set",
1255 "xyz.openbmc_project.User.Attributes",
1256 "UserLockedForFailedAttempt", std::variant<bool>{*locked});
1257 }
1258 });
1259}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001260
Ed Tanous6c51eab2021-06-03 12:30:29 -07001261inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001262{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001263
Ed Tanous6c51eab2021-06-03 12:30:29 -07001264 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001265 .privileges(redfish::privileges::getAccountService)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001266 .methods(
Abhishek Patel72048782021-06-02 09:53:24 -05001267 boost::beast::http::verb::get)([](const crow::Request& req,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001268 const std::shared_ptr<
1269 bmcweb::AsyncResp>& asyncResp)
1270 -> void {
1271 const persistent_data::AuthConfigMethods& authMethodsConfig =
1272 persistent_data::SessionStore::getInstance()
1273 .getAuthMethodsConfig();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001274
Ed Tanous6c51eab2021-06-03 12:30:29 -07001275 asyncResp->res.jsonValue = {
1276 {"@odata.id", "/redfish/v1/AccountService"},
1277 {"@odata.type", "#AccountService."
1278 "v1_5_0.AccountService"},
1279 {"Id", "AccountService"},
1280 {"Name", "Account Service"},
1281 {"Description", "Account Service"},
1282 {"ServiceEnabled", true},
1283 {"MaxPasswordLength", 20},
1284 {"Accounts",
1285 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
1286 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
1287 {"Oem",
1288 {{"OpenBMC",
1289 {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
Ed Tanousd8e3c292021-09-21 18:01:43 -07001290 {"@odata.id", "/redfish/v1/AccountService#/Oem/OpenBMC"},
Ed Tanous6c51eab2021-06-03 12:30:29 -07001291 {"AuthMethods",
1292 {
1293 {"BasicAuth", authMethodsConfig.basic},
1294 {"SessionToken", authMethodsConfig.sessionToken},
1295 {"XToken", authMethodsConfig.xtoken},
1296 {"Cookie", authMethodsConfig.cookie},
1297 {"TLS", authMethodsConfig.tls},
Abhishek Patel72048782021-06-02 09:53:24 -05001298 }}}}}}};
1299 // /redfish/v1/AccountService/LDAP/Certificates is something only
1300 // ConfigureManager can access then only display when the user has
1301 // permissions ConfigureManager
1302 Privileges effectiveUserPrivileges =
1303 redfish::getUserPrivileges(req.userRole);
1304
1305 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1306 effectiveUserPrivileges))
1307 {
1308 asyncResp->res.jsonValue["LDAP"] = {
1309 {"Certificates",
1310 {{"@odata.id",
1311 "/redfish/v1/AccountService/LDAP/Certificates"}}}};
1312 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001313 crow::connections::systemBus->async_method_call(
1314 [asyncResp](
1315 const boost::system::error_code ec,
1316 const std::vector<
1317 std::pair<std::string,
1318 std::variant<uint32_t, uint16_t, uint8_t>>>&
1319 propertiesList) {
1320 if (ec)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001321 {
1322 messages::internalError(asyncResp->res);
1323 return;
1324 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001325 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1326 << "properties for AccountService";
1327 for (const std::pair<std::string,
1328 std::variant<uint32_t, uint16_t,
1329 uint8_t>>& property :
1330 propertiesList)
1331 {
1332 if (property.first == "MinPasswordLength")
1333 {
1334 const uint8_t* value =
1335 std::get_if<uint8_t>(&property.second);
1336 if (value != nullptr)
Ratan Gupta24c85422019-01-30 19:41:24 +05301337 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001338 asyncResp->res.jsonValue["MinPasswordLength"] =
1339 *value;
Ratan Gupta24c85422019-01-30 19:41:24 +05301340 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001341 }
1342 if (property.first == "AccountUnlockTimeout")
1343 {
1344 const uint32_t* value =
1345 std::get_if<uint32_t>(&property.second);
1346 if (value != nullptr)
1347 {
1348 asyncResp->res
1349 .jsonValue["AccountLockoutDuration"] =
1350 *value;
1351 }
1352 }
1353 if (property.first == "MaxLoginAttemptBeforeLockout")
1354 {
1355 const uint16_t* value =
1356 std::get_if<uint16_t>(&property.second);
1357 if (value != nullptr)
1358 {
1359 asyncResp->res
1360 .jsonValue["AccountLockoutThreshold"] =
1361 *value;
1362 }
1363 }
1364 }
1365 },
1366 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1367 "org.freedesktop.DBus.Properties", "GetAll",
1368 "xyz.openbmc_project.User.AccountPolicy");
1369
1370 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
1371 const std::string& ldapType) {
1372 if (!success)
1373 {
1374 return;
1375 }
1376 parseLDAPConfigData(asyncResp->res.jsonValue, confData,
1377 ldapType);
1378 };
1379
1380 getLDAPConfigData("LDAP", callback);
1381 getLDAPConfigData("ActiveDirectory", callback);
1382 });
1383
Ed Tanousf5ffd802021-07-19 10:55:33 -07001384 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
1385 .privileges(redfish::privileges::getAccountService)
1386 .methods(boost::beast::http::verb::patch)(
1387 [](const crow::Request& req,
1388 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1389 std::optional<uint32_t> unlockTimeout;
1390 std::optional<uint16_t> lockoutThreshold;
1391 std::optional<uint16_t> minPasswordLength;
1392 std::optional<uint16_t> maxPasswordLength;
1393 std::optional<nlohmann::json> ldapObject;
1394 std::optional<nlohmann::json> activeDirectoryObject;
1395 std::optional<nlohmann::json> oemObject;
1396
1397 if (!json_util::readJson(
1398 req, asyncResp->res, "AccountLockoutDuration",
1399 unlockTimeout, "AccountLockoutThreshold",
1400 lockoutThreshold, "MaxPasswordLength",
1401 maxPasswordLength, "MinPasswordLength",
1402 minPasswordLength, "LDAP", ldapObject,
1403 "ActiveDirectory", activeDirectoryObject, "Oem",
1404 oemObject))
1405 {
1406 return;
1407 }
1408
1409 if (minPasswordLength)
1410 {
1411 messages::propertyNotWritable(asyncResp->res,
1412 "MinPasswordLength");
1413 }
1414
1415 if (maxPasswordLength)
1416 {
1417 messages::propertyNotWritable(asyncResp->res,
1418 "MaxPasswordLength");
1419 }
1420
1421 if (ldapObject)
1422 {
1423 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1424 }
1425
1426 if (std::optional<nlohmann::json> oemOpenBMCObject;
1427 oemObject &&
1428 json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1429 oemOpenBMCObject))
1430 {
1431 if (std::optional<nlohmann::json> authMethodsObject;
1432 oemOpenBMCObject &&
1433 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1434 "AuthMethods", authMethodsObject))
1435 {
1436 if (authMethodsObject)
1437 {
1438 handleAuthMethodsPatch(*authMethodsObject,
1439 asyncResp);
1440 }
1441 }
1442 }
1443
1444 if (activeDirectoryObject)
1445 {
1446 handleLDAPPatch(*activeDirectoryObject, asyncResp,
1447 "ActiveDirectory");
1448 }
1449
1450 if (unlockTimeout)
1451 {
1452 crow::connections::systemBus->async_method_call(
1453 [asyncResp](const boost::system::error_code ec) {
1454 if (ec)
1455 {
1456 messages::internalError(asyncResp->res);
1457 return;
1458 }
1459 messages::success(asyncResp->res);
1460 },
1461 "xyz.openbmc_project.User.Manager",
1462 "/xyz/openbmc_project/user",
1463 "org.freedesktop.DBus.Properties", "Set",
1464 "xyz.openbmc_project.User.AccountPolicy",
1465 "AccountUnlockTimeout",
1466 std::variant<uint32_t>(*unlockTimeout));
1467 }
1468 if (lockoutThreshold)
1469 {
1470 crow::connections::systemBus->async_method_call(
1471 [asyncResp](const boost::system::error_code ec) {
1472 if (ec)
1473 {
1474 messages::internalError(asyncResp->res);
1475 return;
1476 }
1477 messages::success(asyncResp->res);
1478 },
1479 "xyz.openbmc_project.User.Manager",
1480 "/xyz/openbmc_project/user",
1481 "org.freedesktop.DBus.Properties", "Set",
1482 "xyz.openbmc_project.User.AccountPolicy",
1483 "MaxLoginAttemptBeforeLockout",
1484 std::variant<uint16_t>(*lockoutThreshold));
1485 }
1486 });
1487
Ed Tanous6c51eab2021-06-03 12:30:29 -07001488 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001489 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001490 .methods(boost::beast::http::verb::get)(
1491 [](const crow::Request& req,
1492 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1493 asyncResp->res.jsonValue = {
1494 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
1495 {"@odata.type", "#ManagerAccountCollection."
1496 "ManagerAccountCollection"},
1497 {"Name", "Accounts Collection"},
1498 {"Description", "BMC User Accounts"}};
1499
Ed Tanous6c51eab2021-06-03 12:30:29 -07001500 Privileges effectiveUserPrivileges =
1501 redfish::getUserPrivileges(req.userRole);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001502
1503 std::string thisUser = req.session->username;
1504
1505 crow::connections::systemBus->async_method_call(
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001506 [asyncResp, thisUser, effectiveUserPrivileges](
1507 const boost::system::error_code ec,
1508 const ManagedObjectType& users) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001509 if (ec)
1510 {
1511 messages::internalError(asyncResp->res);
Ratan Gupta24c85422019-01-30 19:41:24 +05301512 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001513 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001514
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001515 bool userCanSeeAllAccounts =
1516 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001517 {"ConfigureUsers"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001518
1519 bool userCanSeeSelf =
1520 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001521 {"ConfigureSelf"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001522
Ed Tanous6c51eab2021-06-03 12:30:29 -07001523 nlohmann::json& memberArray =
1524 asyncResp->res.jsonValue["Members"];
1525 memberArray = nlohmann::json::array();
Ratan Gupta24c85422019-01-30 19:41:24 +05301526
Ed Tanous6c51eab2021-06-03 12:30:29 -07001527 for (auto& userpath : users)
1528 {
1529 std::string user = userpath.first.filename();
1530 if (user.empty())
Ratan Gupta24c85422019-01-30 19:41:24 +05301531 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301532 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001533 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1534
Ratan Gupta24c85422019-01-30 19:41:24 +05301535 return;
1536 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301537
Ed Tanous6c51eab2021-06-03 12:30:29 -07001538 // As clarified by Redfish here:
1539 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1540 // Users without ConfigureUsers, only see their own
1541 // account. Users with ConfigureUsers, see all
1542 // accounts.
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001543 if (userCanSeeAllAccounts ||
1544 (thisUser == user && userCanSeeSelf))
Ratan Gupta24c85422019-01-30 19:41:24 +05301545 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001546 memberArray.push_back(
1547 {{"@odata.id",
1548 "/redfish/v1/AccountService/Accounts/" +
1549 user}});
Ratan Gupta24c85422019-01-30 19:41:24 +05301550 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001551 }
1552 asyncResp->res.jsonValue["Members@odata.count"] =
1553 memberArray.size();
1554 },
1555 "xyz.openbmc_project.User.Manager",
1556 "/xyz/openbmc_project/user",
1557 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Ratan Gupta24c85422019-01-30 19:41:24 +05301558 });
Ed Tanous06e086d2018-09-19 17:19:52 -07001559
Ed Tanous6c51eab2021-06-03 12:30:29 -07001560 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001561 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001562 .methods(boost::beast::http::verb::post)([](const crow::Request& req,
1563 const std::shared_ptr<
1564 bmcweb::AsyncResp>&
1565 asyncResp) -> void {
1566 std::string username;
1567 std::string password;
1568 std::optional<std::string> roleId("User");
1569 std::optional<bool> enabled = true;
1570 if (!json_util::readJson(req, asyncResp->res, "UserName", username,
1571 "Password", password, "RoleId", roleId,
1572 "Enabled", enabled))
1573 {
1574 return;
1575 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001576
Ed Tanous6c51eab2021-06-03 12:30:29 -07001577 std::string priv = getPrivilegeFromRoleId(*roleId);
1578 if (priv.empty())
1579 {
1580 messages::propertyValueNotInList(asyncResp->res, *roleId,
1581 "RoleId");
1582 return;
1583 }
1584 // TODO: Following override will be reverted once support in
1585 // phosphor-user-manager is added. In order to avoid dependency
1586 // issues, this is added in bmcweb, which will removed, once
1587 // phosphor-user-manager supports priv-noaccess.
1588 if (priv == "priv-noaccess")
1589 {
1590 roleId = "";
1591 }
1592 else
1593 {
1594 roleId = priv;
1595 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001596
Ed Tanous6c51eab2021-06-03 12:30:29 -07001597 // Reading AllGroups property
1598 crow::connections::systemBus->async_method_call(
1599 [asyncResp, username, password{std::move(password)}, roleId,
1600 enabled](
1601 const boost::system::error_code ec,
1602 const std::variant<std::vector<std::string>>& allGroups) {
1603 if (ec)
1604 {
1605 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1606 messages::internalError(asyncResp->res);
1607 return;
1608 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001609
Ed Tanous6c51eab2021-06-03 12:30:29 -07001610 const std::vector<std::string>* allGroupsList =
1611 std::get_if<std::vector<std::string>>(&allGroups);
1612
1613 if (allGroupsList == nullptr || allGroupsList->empty())
1614 {
1615 messages::internalError(asyncResp->res);
1616 return;
1617 }
1618
1619 crow::connections::systemBus->async_method_call(
1620 [asyncResp, username,
1621 password](const boost::system::error_code ec2,
1622 sdbusplus::message::message& m) {
1623 if (ec2)
1624 {
1625 userErrorMessageHandler(
1626 m.get_error(), asyncResp, username, "");
1627 return;
1628 }
1629
1630 if (pamUpdatePassword(username, password) !=
1631 PAM_SUCCESS)
1632 {
1633 // At this point we have a user that's been
1634 // created, but the password set
1635 // failed.Something is wrong, so delete the user
1636 // that we've already created
1637 crow::connections::systemBus->async_method_call(
1638 [asyncResp, password](
1639 const boost::system::error_code ec3) {
1640 if (ec3)
1641 {
1642 messages::internalError(
1643 asyncResp->res);
1644 return;
1645 }
1646
1647 // If password is invalid
1648 messages::propertyValueFormatError(
1649 asyncResp->res, password,
1650 "Password");
1651 },
1652 "xyz.openbmc_project.User.Manager",
1653 "/xyz/openbmc_project/user/" + username,
1654 "xyz.openbmc_project.Object.Delete",
1655 "Delete");
1656
1657 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1658 return;
1659 }
1660
1661 messages::created(asyncResp->res);
1662 asyncResp->res.addHeader(
1663 "Location",
1664 "/redfish/v1/AccountService/Accounts/" +
1665 username);
1666 },
1667 "xyz.openbmc_project.User.Manager",
1668 "/xyz/openbmc_project/user",
1669 "xyz.openbmc_project.User.Manager", "CreateUser",
1670 username, *allGroupsList, *roleId, *enabled);
1671 },
1672 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1673 "org.freedesktop.DBus.Properties", "Get",
1674 "xyz.openbmc_project.User.Manager", "AllGroups");
1675 });
1676
1677 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001678 .privileges(redfish::privileges::getManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001679 .methods(
1680 boost::beast::http::verb::
1681 get)([](const crow::Request& req,
1682 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1683 const std::string& accountName) -> void {
1684 if (req.session->username != accountName)
1685 {
1686 // At this point we've determined that the user is trying to
1687 // modify a user that isn't them. We need to verify that they
1688 // have permissions to modify other users, so re-run the auth
1689 // check with the same permissions, minus ConfigureSelf.
1690 Privileges effectiveUserPrivileges =
1691 redfish::getUserPrivileges(req.userRole);
1692 Privileges requiredPermissionsToChangeNonSelf = {
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001693 "ConfigureUsers", "ConfigureManager"};
Ed Tanous6c51eab2021-06-03 12:30:29 -07001694 if (!effectiveUserPrivileges.isSupersetOf(
1695 requiredPermissionsToChangeNonSelf))
Ed Tanous06e086d2018-09-19 17:19:52 -07001696 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001697 BMCWEB_LOG_DEBUG << "GET Account denied access";
1698 messages::insufficientPrivilege(asyncResp->res);
1699 return;
1700 }
1701 }
1702
1703 crow::connections::systemBus->async_method_call(
1704 [asyncResp, accountName](const boost::system::error_code ec,
1705 const ManagedObjectType& users) {
1706 if (ec)
1707 {
1708 messages::internalError(asyncResp->res);
1709 return;
1710 }
1711 auto userIt = users.begin();
1712
1713 for (; userIt != users.end(); userIt++)
1714 {
1715 if (boost::ends_with(userIt->first.str,
1716 "/" + accountName))
1717 {
1718 break;
1719 }
1720 }
1721 if (userIt == users.end())
1722 {
1723 messages::resourceNotFound(
1724 asyncResp->res, "ManagerAccount", accountName);
1725 return;
1726 }
1727
1728 asyncResp->res.jsonValue = {
1729 {"@odata.type",
1730 "#ManagerAccount.v1_4_0.ManagerAccount"},
1731 {"Name", "User Account"},
1732 {"Description", "User Account"},
1733 {"Password", nullptr},
1734 {"AccountTypes", {"Redfish"}}};
1735
1736 for (const auto& interface : userIt->second)
1737 {
1738 if (interface.first ==
1739 "xyz.openbmc_project.User.Attributes")
1740 {
1741 for (const auto& property : interface.second)
1742 {
1743 if (property.first == "UserEnabled")
1744 {
1745 const bool* userEnabled =
1746 std::get_if<bool>(&property.second);
1747 if (userEnabled == nullptr)
1748 {
1749 BMCWEB_LOG_ERROR
1750 << "UserEnabled wasn't a bool";
1751 messages::internalError(asyncResp->res);
1752 return;
1753 }
1754 asyncResp->res.jsonValue["Enabled"] =
1755 *userEnabled;
1756 }
1757 else if (property.first ==
1758 "UserLockedForFailedAttempt")
1759 {
1760 const bool* userLocked =
1761 std::get_if<bool>(&property.second);
1762 if (userLocked == nullptr)
1763 {
1764 BMCWEB_LOG_ERROR << "UserLockedForF"
1765 "ailedAttempt "
1766 "wasn't a bool";
1767 messages::internalError(asyncResp->res);
1768 return;
1769 }
1770 asyncResp->res.jsonValue["Locked"] =
1771 *userLocked;
1772 asyncResp->res.jsonValue
1773 ["Locked@Redfish.AllowableValues"] = {
1774 "false"}; // can only unlock accounts
1775 }
1776 else if (property.first == "UserPrivilege")
1777 {
1778 const std::string* userPrivPtr =
1779 std::get_if<std::string>(
1780 &property.second);
1781 if (userPrivPtr == nullptr)
1782 {
1783 BMCWEB_LOG_ERROR
1784 << "UserPrivilege wasn't a "
1785 "string";
1786 messages::internalError(asyncResp->res);
1787 return;
1788 }
1789 std::string role =
1790 getRoleIdFromPrivilege(*userPrivPtr);
1791 if (role.empty())
1792 {
1793 BMCWEB_LOG_ERROR << "Invalid user role";
1794 messages::internalError(asyncResp->res);
1795 return;
1796 }
1797 asyncResp->res.jsonValue["RoleId"] = role;
1798
1799 asyncResp->res.jsonValue["Links"]["Role"] =
1800 {{"@odata.id",
1801 "/redfish/v1/AccountService/"
1802 "Roles/" +
1803 role}};
1804 }
1805 else if (property.first ==
1806 "UserPasswordExpired")
1807 {
1808 const bool* userPasswordExpired =
1809 std::get_if<bool>(&property.second);
1810 if (userPasswordExpired == nullptr)
1811 {
1812 BMCWEB_LOG_ERROR << "UserPassword"
1813 "Expired "
1814 "wasn't a bool";
1815 messages::internalError(asyncResp->res);
1816 return;
1817 }
1818 asyncResp->res
1819 .jsonValue["PasswordChangeRequired"] =
1820 *userPasswordExpired;
1821 }
1822 }
1823 }
1824 }
1825
1826 asyncResp->res.jsonValue["@odata.id"] =
1827 "/redfish/v1/AccountService/Accounts/" + accountName;
1828 asyncResp->res.jsonValue["Id"] = accountName;
1829 asyncResp->res.jsonValue["UserName"] = accountName;
1830 },
1831 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1832 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1833 });
1834
1835 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001836 // TODO this privilege should be using the generated endpoints, but
1837 // because of the special handling of ConfigureSelf, it's not able to
1838 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001839 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1840 .methods(boost::beast::http::verb::patch)(
1841 [](const crow::Request& req,
1842 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1843 const std::string& username) -> void {
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 if (!json_util::readJson(req, asyncResp->res, "UserName",
1850 newUserName, "Password", password,
1851 "RoleId", roleId, "Enabled", enabled,
1852 "Locked", locked))
1853 {
Ed Tanous06e086d2018-09-19 17:19:52 -07001854 return;
1855 }
1856
Ed Tanous6c51eab2021-06-03 12:30:29 -07001857 // Perform a proper ConfigureSelf authority check. If the
1858 // session is being used to PATCH a property other than
1859 // Password, then the ConfigureSelf privilege does not apply.
1860 // If the user is operating on an account not their own, then
1861 // their ConfigureSelf privilege does not apply. In either
1862 // case, perform the authority check again without the user's
1863 // ConfigureSelf privilege.
1864 if ((username != req.session->username))
1865 {
1866 Privileges requiredPermissionsToChangeNonSelf = {
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001867 "ConfigureUsers"};
Ed Tanous6c51eab2021-06-03 12:30:29 -07001868 Privileges effectiveUserPrivileges =
1869 redfish::getUserPrivileges(req.userRole);
1870
1871 if (!effectiveUserPrivileges.isSupersetOf(
1872 requiredPermissionsToChangeNonSelf))
1873 {
1874 messages::insufficientPrivilege(asyncResp->res);
1875 return;
1876 }
1877 }
1878
1879 // if user name is not provided in the patch method or if it
1880 // matches the user name in the URI, then we are treating it as
1881 // updating user properties other then username. If username
1882 // provided doesn't match the URI, then we are treating this as
1883 // user rename request.
1884 if (!newUserName || (newUserName.value() == username))
1885 {
1886 updateUserProperties(asyncResp, username, password, enabled,
1887 roleId, locked);
1888 return;
1889 }
1890 crow::connections::systemBus->async_method_call(
1891 [asyncResp, username, password(std::move(password)),
1892 roleId(std::move(roleId)), enabled,
1893 newUser{std::string(*newUserName)},
1894 locked](const boost::system::error_code ec,
1895 sdbusplus::message::message& m) {
1896 if (ec)
1897 {
1898 userErrorMessageHandler(m.get_error(), asyncResp,
1899 newUser, username);
1900 return;
1901 }
1902
1903 updateUserProperties(asyncResp, newUser, password,
1904 enabled, roleId, locked);
1905 },
1906 "xyz.openbmc_project.User.Manager",
1907 "/xyz/openbmc_project/user",
1908 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1909 *newUserName);
1910 });
1911
1912 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001913 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001914 .methods(boost::beast::http::verb::delete_)(
1915 [](const crow::Request& /*req*/,
1916 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1917 const std::string& username) -> void {
1918 const std::string userPath =
1919 "/xyz/openbmc_project/user/" + username;
1920
1921 crow::connections::systemBus->async_method_call(
1922 [asyncResp, username](const boost::system::error_code ec) {
1923 if (ec)
1924 {
1925 messages::resourceNotFound(
1926 asyncResp->res,
1927 "#ManagerAccount.v1_4_0.ManagerAccount",
1928 username);
1929 return;
1930 }
1931
1932 messages::accountRemoved(asyncResp->res);
1933 },
1934 "xyz.openbmc_project.User.Manager", userPath,
1935 "xyz.openbmc_project.Object.Delete", "Delete");
1936 });
1937}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001938
Ed Tanous1abe55e2018-09-05 08:30:59 -07001939} // namespace redfish