blob: 46f6a93b729a09830bec29cbdc9b0b0b642bf827 [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 Tanous1abe55e2018-09-05 08:30:59 -070026namespace redfish
27{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010028
Ed Tanous23a21a12020-07-25 04:45:05 +000029constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053030 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070031constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053032 "/xyz/openbmc_project/user/ldap/active_directory";
33
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053034constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053035constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
36constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
37constexpr const char* ldapConfigInterface =
38 "xyz.openbmc_project.User.Ldap.Config";
39constexpr const char* ldapCreateInterface =
40 "xyz.openbmc_project.User.Ldap.Create";
41constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053042constexpr const char* ldapPrivMapperInterface =
43 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053044constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
45constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
46constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
47constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
48constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
49
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060050struct LDAPRoleMapData
51{
52 std::string groupName;
53 std::string privilege;
54};
55
Ratan Gupta6973a582018-12-13 18:25:44 +053056struct LDAPConfigData
57{
58 std::string uri{};
59 std::string bindDN{};
60 std::string baseDN{};
61 std::string searchScope{};
62 std::string serverType{};
63 bool serviceEnabled = false;
64 std::string userNameAttribute{};
65 std::string groupAttribute{};
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060066 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053067};
68
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020069using DbusInterfaceType = boost::container::flat_map<
Ed Tanous168e20c2021-12-13 14:39:53 -080070 std::string,
71 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>>;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020072
73using ManagedObjectType =
74 std::vector<std::pair<sdbusplus::message::object_path, DbusInterfaceType>>;
75
Ratan Gupta6973a582018-12-13 18:25:44 +053076using GetObjectType =
77 std::vector<std::pair<std::string, std::vector<std::string>>>;
AppaRao Puli84e12cb2018-10-11 01:28:15 +053078
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060079inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053080{
81 if (role == "priv-admin")
82 {
83 return "Administrator";
84 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070085 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053086 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053087 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053088 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070089 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053090 {
91 return "Operator";
92 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070093 if ((role == "") || (role == "priv-noaccess"))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +000094 {
95 return "NoAccess";
96 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +053097 return "";
98}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060099inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530100{
101 if (role == "Administrator")
102 {
103 return "priv-admin";
104 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700105 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530106 {
107 return "priv-user";
108 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700109 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530110 {
111 return "priv-operator";
112 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700113 if ((role == "NoAccess") || (role == ""))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +0000114 {
115 return "priv-noaccess";
116 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530117 return "";
118}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700119
zhanghch058d1b46d2021-04-01 11:18:24 +0800120inline void userErrorMessageHandler(
121 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
122 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000123{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000124 if (e == nullptr)
125 {
126 messages::internalError(asyncResp->res);
127 return;
128 }
129
Manojkiran Eda055806b2020-11-03 09:36:28 +0530130 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000131 if (strcmp(errorMessage,
132 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
133 {
134 messages::resourceAlreadyExists(asyncResp->res,
Gunnar Mills8114bd42020-06-11 20:55:21 -0500135 "#ManagerAccount.v1_4_0.ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000136 "UserName", newUser);
137 }
138 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
139 "UserNameDoesNotExist") == 0)
140 {
141 messages::resourceNotFound(
Gunnar Mills8114bd42020-06-11 20:55:21 -0500142 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000143 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700144 else if ((strcmp(errorMessage,
145 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
146 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800147 (strcmp(
148 errorMessage,
149 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
150 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",
Ed Tanous168e20c2021-12-13 14:39:53 -0800295 dbus::utility::DbusVariantType(
296 std::move(*remoteGroup)));
Ratan Gupta06785242019-07-26 22:30:16 +0530297 }
298
299 // If "LocalRole" info is provided
300 if (localRole)
301 {
302 crow::connections::systemBus->async_method_call(
303 [asyncResp, roleMapObjData, serverType, index,
304 localRole](const boost::system::error_code ec) {
305 if (ec)
306 {
307 BMCWEB_LOG_ERROR << "DBUS response error: "
308 << ec;
309 messages::internalError(asyncResp->res);
310 return;
311 }
312 asyncResp->res
313 .jsonValue[serverType]["RemoteRoleMapping"]
314 [index]["LocalRole"] = *localRole;
315 },
316 ldapDbusService, roleMapObjData[index].first,
317 propertyInterface, "Set",
318 "xyz.openbmc_project.User.PrivilegeMapperEntry",
319 "Privilege",
Ed Tanous168e20c2021-12-13 14:39:53 -0800320 dbus::utility::DbusVariantType(
Ratan Gupta06785242019-07-26 22:30:16 +0530321 getPrivilegeFromRoleId(std::move(*localRole))));
322 }
323 }
324 // Create a new RoleMapping Object.
325 else
326 {
327 BMCWEB_LOG_DEBUG
328 << "setRoleMappingProperties: Creating new Object";
329 std::string pathString =
330 "RemoteRoleMapping/" + std::to_string(index);
331
332 if (!localRole)
333 {
334 messages::propertyMissing(asyncResp->res,
335 pathString + "/LocalRole");
336 continue;
337 }
338 if (!remoteGroup)
339 {
340 messages::propertyMissing(asyncResp->res,
341 pathString + "/RemoteGroup");
342 continue;
343 }
344
345 std::string dbusObjectPath;
346 if (serverType == "ActiveDirectory")
347 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700348 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530349 }
350 else if (serverType == "LDAP")
351 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000352 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530353 }
354
355 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
356 << ",LocalRole=" << *localRole;
357
358 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700359 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530360 remoteGroup](const boost::system::error_code ec) {
361 if (ec)
362 {
363 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
364 messages::internalError(asyncResp->res);
365 return;
366 }
367 nlohmann::json& remoteRoleJson =
368 asyncResp->res
369 .jsonValue[serverType]["RemoteRoleMapping"];
370 remoteRoleJson.push_back(
371 {{"LocalRole", *localRole},
372 {"RemoteGroup", *remoteGroup}});
373 },
374 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700375 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530376 getPrivilegeFromRoleId(std::move(*localRole)));
377 }
378 }
379 }
380}
381
382/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530383 * Function that retrieves all properties for LDAP config object
384 * into JSON
385 */
386template <typename CallbackFunc>
387inline void getLDAPConfigData(const std::string& ldapType,
388 CallbackFunc&& callback)
389{
Ratan Guptaab828d72019-04-22 14:18:33 +0530390
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600391 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530392 ldapConfigInterface};
393
394 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600395 [callback, ldapType](const boost::system::error_code ec,
396 const GetObjectType& resp) {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600397 if (ec || resp.empty())
398 {
George Liu0fda0f12021-11-16 10:06:17 +0800399 BMCWEB_LOG_ERROR
400 << "DBUS response error during getting of service name: "
401 << ec;
Ed Tanous23a21a12020-07-25 04:45:05 +0000402 LDAPConfigData empty{};
403 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600404 return;
405 }
406 std::string service = resp.begin()->first;
407 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000408 [callback, ldapType](const boost::system::error_code errorCode,
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600409 const ManagedObjectType& ldapObjects) {
410 LDAPConfigData confData{};
Ed Tanous81ce6092020-12-17 16:54:55 +0000411 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600412 {
413 callback(false, confData, ldapType);
414 BMCWEB_LOG_ERROR << "D-Bus responses error: "
Ed Tanous81ce6092020-12-17 16:54:55 +0000415 << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600416 return;
417 }
418
419 std::string ldapDbusType;
420 std::string searchString;
421
422 if (ldapType == "LDAP")
423 {
George Liu0fda0f12021-11-16 10:06:17 +0800424 ldapDbusType =
425 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600426 searchString = "openldap";
427 }
428 else if (ldapType == "ActiveDirectory")
429 {
430 ldapDbusType =
George Liu0fda0f12021-11-16 10:06:17 +0800431 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600432 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 }
George Liu0fda0f12021-11-16 10:06:17 +0800521 else if (
522 interface.first ==
523 "xyz.openbmc_project.User.PrivilegeMapperEntry")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600524 {
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",
Ed Tanous168e20c2021-12-13 14:39:53 -0800666 dbus::utility::DbusVariantType(serviceAddressList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700667}
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",
Ed Tanous168e20c2021-12-13 14:39:53 -0800697 ldapConfigInterface, "LDAPBindDN",
698 dbus::utility::DbusVariantType(username));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700699}
700
701/**
702 * @brief updates the LDAP password
703 * @param password : ldap password which needs to be updated.
704 * @param asyncResp pointer to the JSON response
705 * @param ldapServerElementName Type of LDAP
706 * server(openLDAP/ActiveDirectory)
707 */
708
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700709inline void
710 handlePasswordPatch(const std::string& password,
711 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
712 const std::string& ldapServerElementName,
713 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700714{
715 crow::connections::systemBus->async_method_call(
716 [asyncResp, password,
717 ldapServerElementName](const boost::system::error_code ec) {
718 if (ec)
719 {
720 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
721 messages::internalError(asyncResp->res);
722 return;
723 }
724 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
725 ["Password"] = "";
726 BMCWEB_LOG_DEBUG << "Updated the password";
727 },
728 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
729 ldapConfigInterface, "LDAPBindDNPassword",
Ed Tanous168e20c2021-12-13 14:39:53 -0800730 dbus::utility::DbusVariantType(password));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700731}
732
733/**
734 * @brief updates the LDAP BaseDN and updates the
735 json response with the new value.
736 * @param baseDNList baseDN list which needs to be updated.
737 * @param asyncResp pointer to the JSON response
738 * @param ldapServerElementName Type of LDAP
739 server(openLDAP/ActiveDirectory)
740 */
741
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700742inline void
743 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
744 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
745 const std::string& ldapServerElementName,
746 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700747{
748 crow::connections::systemBus->async_method_call(
749 [asyncResp, baseDNList,
750 ldapServerElementName](const boost::system::error_code ec) {
751 if (ec)
752 {
753 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
754 messages::internalError(asyncResp->res);
755 return;
756 }
757 auto& serverTypeJson =
758 asyncResp->res.jsonValue[ldapServerElementName];
759 auto& searchSettingsJson =
760 serverTypeJson["LDAPService"]["SearchSettings"];
761 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
762 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
763 if (baseDNList.size() > 1)
764 {
765 messages::propertyValueModified(asyncResp->res,
766 "BaseDistinguishedNames",
767 baseDNList.front());
768 }
769 BMCWEB_LOG_DEBUG << "Updated the base DN";
770 },
771 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
772 ldapConfigInterface, "LDAPBaseDN",
Ed Tanous168e20c2021-12-13 14:39:53 -0800773 dbus::utility::DbusVariantType(baseDNList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700774}
775/**
776 * @brief updates the LDAP user name attribute and updates the
777 json response with the new value.
778 * @param userNameAttribute attribute to be updated.
779 * @param asyncResp pointer to the JSON response
780 * @param ldapServerElementName Type of LDAP
781 server(openLDAP/ActiveDirectory)
782 */
783
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700784inline void
785 handleUserNameAttrPatch(const std::string& userNameAttribute,
786 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
787 const std::string& ldapServerElementName,
788 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700789{
790 crow::connections::systemBus->async_method_call(
791 [asyncResp, userNameAttribute,
792 ldapServerElementName](const boost::system::error_code ec) {
793 if (ec)
794 {
795 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
796 "username attribute";
797 messages::internalError(asyncResp->res);
798 return;
799 }
800 auto& serverTypeJson =
801 asyncResp->res.jsonValue[ldapServerElementName];
802 auto& searchSettingsJson =
803 serverTypeJson["LDAPService"]["SearchSettings"];
804 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
805 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
806 },
807 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
808 ldapConfigInterface, "UserNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800809 dbus::utility::DbusVariantType(userNameAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700810}
811/**
812 * @brief updates the LDAP group attribute and updates the
813 json response with the new value.
814 * @param groupsAttribute attribute to be updated.
815 * @param asyncResp pointer to the JSON response
816 * @param ldapServerElementName Type of LDAP
817 server(openLDAP/ActiveDirectory)
818 */
819
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700820inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700821 const std::string& groupsAttribute,
822 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
823 const std::string& ldapServerElementName,
824 const std::string& ldapConfigObject)
825{
826 crow::connections::systemBus->async_method_call(
827 [asyncResp, groupsAttribute,
828 ldapServerElementName](const boost::system::error_code ec) {
829 if (ec)
830 {
831 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
832 "groupname attribute";
833 messages::internalError(asyncResp->res);
834 return;
835 }
836 auto& serverTypeJson =
837 asyncResp->res.jsonValue[ldapServerElementName];
838 auto& searchSettingsJson =
839 serverTypeJson["LDAPService"]["SearchSettings"];
840 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
841 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
842 },
843 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
844 ldapConfigInterface, "GroupNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800845 dbus::utility::DbusVariantType(groupsAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700846}
847/**
848 * @brief updates the LDAP service enable and updates the
849 json response with the new value.
850 * @param input JSON data.
851 * @param asyncResp pointer to the JSON response
852 * @param ldapServerElementName Type of LDAP
853 server(openLDAP/ActiveDirectory)
854 */
855
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700856inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700857 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
858 const std::string& ldapServerElementName,
859 const std::string& ldapConfigObject)
860{
861 crow::connections::systemBus->async_method_call(
862 [asyncResp, serviceEnabled,
863 ldapServerElementName](const boost::system::error_code ec) {
864 if (ec)
865 {
866 BMCWEB_LOG_DEBUG
867 << "Error Occurred in Updating the service enable";
868 messages::internalError(asyncResp->res);
869 return;
870 }
871 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
872 serviceEnabled;
873 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
874 },
875 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800876 ldapEnableInterface, "Enabled",
877 dbus::utility::DbusVariantType(serviceEnabled));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700878}
879
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700880inline void
881 handleAuthMethodsPatch(nlohmann::json& input,
882 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700883{
884 std::optional<bool> basicAuth;
885 std::optional<bool> cookie;
886 std::optional<bool> sessionToken;
887 std::optional<bool> xToken;
888 std::optional<bool> tls;
889
890 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
891 "Cookie", cookie, "SessionToken", sessionToken,
892 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530893 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700894 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
895 return;
896 }
897
898 // Make a copy of methods configuration
899 persistent_data::AuthConfigMethods authMethodsConfig =
900 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
901
902 if (basicAuth)
903 {
904#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
905 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800906 asyncResp->res,
907 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700908 return;
909#endif
910 authMethodsConfig.basic = *basicAuth;
911 }
912
913 if (cookie)
914 {
915#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800916 messages::actionNotSupported(
917 asyncResp->res,
918 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700919 return;
920#endif
921 authMethodsConfig.cookie = *cookie;
922 }
923
924 if (sessionToken)
925 {
926#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
927 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800928 asyncResp->res,
929 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700930 return;
931#endif
932 authMethodsConfig.sessionToken = *sessionToken;
933 }
934
935 if (xToken)
936 {
937#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800938 messages::actionNotSupported(
939 asyncResp->res,
940 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700941 return;
942#endif
943 authMethodsConfig.xtoken = *xToken;
944 }
945
946 if (tls)
947 {
948#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800949 messages::actionNotSupported(
950 asyncResp->res,
951 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700952 return;
953#endif
954 authMethodsConfig.tls = *tls;
955 }
956
957 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
958 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
959 !authMethodsConfig.tls)
960 {
961 // Do not allow user to disable everything
962 messages::actionNotSupported(asyncResp->res,
963 "of disabling all available methods");
964 return;
965 }
966
967 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
968 authMethodsConfig);
969 // Save configuration immediately
970 persistent_data::getConfig().writeData();
971
972 messages::success(asyncResp->res);
973}
974
975/**
976 * @brief Get the required values from the given JSON, validates the
977 * value and create the LDAP config object.
978 * @param input JSON data
979 * @param asyncResp pointer to the JSON response
980 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
981 */
982
983inline void handleLDAPPatch(nlohmann::json& input,
984 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
985 const std::string& serverType)
986{
987 std::string dbusObjectPath;
988 if (serverType == "ActiveDirectory")
989 {
990 dbusObjectPath = adConfigObject;
991 }
992 else if (serverType == "LDAP")
993 {
994 dbusObjectPath = ldapConfigObjectName;
995 }
996 else
997 {
998 return;
999 }
1000
1001 std::optional<nlohmann::json> authentication;
1002 std::optional<nlohmann::json> ldapService;
1003 std::optional<std::vector<std::string>> serviceAddressList;
1004 std::optional<bool> serviceEnabled;
1005 std::optional<std::vector<std::string>> baseDNList;
1006 std::optional<std::string> userNameAttribute;
1007 std::optional<std::string> groupsAttribute;
1008 std::optional<std::string> userName;
1009 std::optional<std::string> password;
1010 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1011
1012 if (!json_util::readJson(input, asyncResp->res, "Authentication",
1013 authentication, "LDAPService", ldapService,
1014 "ServiceAddresses", serviceAddressList,
1015 "ServiceEnabled", serviceEnabled,
1016 "RemoteRoleMapping", remoteRoleMapData))
1017 {
1018 return;
1019 }
1020
1021 if (authentication)
1022 {
1023 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1024 password);
1025 }
1026 if (ldapService)
1027 {
1028 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1029 userNameAttribute, groupsAttribute);
1030 }
1031 if (serviceAddressList)
1032 {
1033 if ((*serviceAddressList).size() == 0)
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301034 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001035 messages::propertyValueNotInList(asyncResp->res, "[]",
1036 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001037 return;
1038 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001039 }
1040 if (baseDNList)
1041 {
1042 if ((*baseDNList).size() == 0)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301043 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001044 messages::propertyValueNotInList(asyncResp->res, "[]",
1045 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301046 return;
1047 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001048 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301049
Ed Tanous6c51eab2021-06-03 12:30:29 -07001050 // nothing to update, then return
1051 if (!userName && !password && !serviceAddressList && !baseDNList &&
1052 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1053 !remoteRoleMapData)
1054 {
1055 return;
1056 }
1057
1058 // Get the existing resource first then keep modifying
1059 // whenever any property gets updated.
1060 getLDAPConfigData(serverType, [asyncResp, userName, password, baseDNList,
1061 userNameAttribute, groupsAttribute,
1062 serviceAddressList, serviceEnabled,
1063 dbusObjectPath, remoteRoleMapData](
1064 bool success,
1065 const LDAPConfigData& confData,
1066 const std::string& serverT) {
1067 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301068 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001069 messages::internalError(asyncResp->res);
1070 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301071 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001072 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1073 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301074 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001075 // Disable the service first and update the rest of
1076 // the properties.
1077 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301078 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001079
Ratan Gupta8a07d282019-03-16 08:33:47 +05301080 if (serviceAddressList)
1081 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001082 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1083 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301084 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001085 if (userName)
1086 {
1087 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1088 }
1089 if (password)
1090 {
1091 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1092 }
1093
Ratan Gupta8a07d282019-03-16 08:33:47 +05301094 if (baseDNList)
1095 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001096 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1097 }
1098 if (userNameAttribute)
1099 {
1100 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1101 dbusObjectPath);
1102 }
1103 if (groupsAttribute)
1104 {
1105 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1106 dbusObjectPath);
1107 }
1108 if (serviceEnabled)
1109 {
1110 // if user has given the value as true then enable
1111 // the service. if user has given false then no-op
1112 // as service is already stopped.
1113 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301114 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001115 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1116 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301117 }
1118 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001119 else
1120 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001121 // if user has not given the service enabled value
1122 // then revert it to the same state as it was
1123 // before.
1124 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1125 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001126 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001127
Ed Tanous6c51eab2021-06-03 12:30:29 -07001128 if (remoteRoleMapData)
1129 {
1130 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1131 *remoteRoleMapData);
1132 }
1133 });
1134}
1135
1136inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1137 const std::string& username,
1138 std::optional<std::string> password,
1139 std::optional<bool> enabled,
1140 std::optional<std::string> roleId,
1141 std::optional<bool> locked)
1142{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301143 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1144 tempObjPath /= username;
1145 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001146
1147 dbus::utility::checkDbusPathExists(
1148 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001149 [dbusObjectPath, username, password(std::move(password)),
1150 roleId(std::move(roleId)), enabled, locked,
1151 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001152 if (!rc)
1153 {
1154 messages::resourceNotFound(
1155 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1156 username);
1157 return;
1158 }
1159
1160 if (password)
1161 {
1162 int retval = pamUpdatePassword(username, *password);
1163
1164 if (retval == PAM_USER_UNKNOWN)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001165 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001166 messages::resourceNotFound(
1167 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1168 username);
1169 }
1170 else if (retval == PAM_AUTHTOK_ERR)
1171 {
1172 // If password is invalid
1173 messages::propertyValueFormatError(asyncResp->res,
1174 *password, "Password");
1175 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1176 }
1177 else if (retval != PAM_SUCCESS)
1178 {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001179 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001180 return;
1181 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001182 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001183
Ed Tanous6c51eab2021-06-03 12:30:29 -07001184 if (enabled)
1185 {
1186 crow::connections::systemBus->async_method_call(
1187 [asyncResp](const boost::system::error_code ec) {
1188 if (ec)
1189 {
1190 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1191 messages::internalError(asyncResp->res);
1192 return;
1193 }
1194 messages::success(asyncResp->res);
1195 return;
1196 },
1197 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1198 "org.freedesktop.DBus.Properties", "Set",
1199 "xyz.openbmc_project.User.Attributes", "UserEnabled",
Ed Tanous168e20c2021-12-13 14:39:53 -08001200 dbus::utility::DbusVariantType{*enabled});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001201 }
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001202
Ed Tanous6c51eab2021-06-03 12:30:29 -07001203 if (roleId)
1204 {
1205 std::string priv = getPrivilegeFromRoleId(*roleId);
1206 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001207 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001208 messages::propertyValueNotInList(asyncResp->res, *roleId,
1209 "RoleId");
1210 return;
1211 }
1212 if (priv == "priv-noaccess")
1213 {
1214 priv = "";
1215 }
1216
1217 crow::connections::systemBus->async_method_call(
1218 [asyncResp](const boost::system::error_code ec) {
1219 if (ec)
1220 {
1221 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1222 messages::internalError(asyncResp->res);
1223 return;
1224 }
1225 messages::success(asyncResp->res);
1226 },
1227 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1228 "org.freedesktop.DBus.Properties", "Set",
1229 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
Ed Tanous168e20c2021-12-13 14:39:53 -08001230 dbus::utility::DbusVariantType{priv});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001231 }
1232
1233 if (locked)
1234 {
1235 // admin can unlock the account which is locked by
1236 // successive authentication failures but admin should
1237 // not be allowed to lock an account.
1238 if (*locked)
1239 {
1240 messages::propertyValueNotInList(asyncResp->res, "true",
1241 "Locked");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001242 return;
1243 }
1244
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001245 crow::connections::systemBus->async_method_call(
Ed Tanous6c51eab2021-06-03 12:30:29 -07001246 [asyncResp](const boost::system::error_code ec) {
1247 if (ec)
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001248 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001249 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1250 messages::internalError(asyncResp->res);
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001251 return;
1252 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001253 messages::success(asyncResp->res);
1254 return;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001255 },
Ed Tanous6c51eab2021-06-03 12:30:29 -07001256 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1257 "org.freedesktop.DBus.Properties", "Set",
1258 "xyz.openbmc_project.User.Attributes",
Ed Tanous168e20c2021-12-13 14:39:53 -08001259 "UserLockedForFailedAttempt",
1260 dbus::utility::DbusVariantType{*locked});
Ed Tanous6c51eab2021-06-03 12:30:29 -07001261 }
1262 });
1263}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001264
Ed Tanous6c51eab2021-06-03 12:30:29 -07001265inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001266{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001267
Ed Tanous6c51eab2021-06-03 12:30:29 -07001268 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001269 .privileges(redfish::privileges::getAccountService)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001270 .methods(
Abhishek Patel72048782021-06-02 09:53:24 -05001271 boost::beast::http::verb::get)([](const crow::Request& req,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001272 const std::shared_ptr<
1273 bmcweb::AsyncResp>& asyncResp)
1274 -> void {
1275 const persistent_data::AuthConfigMethods& authMethodsConfig =
1276 persistent_data::SessionStore::getInstance()
1277 .getAuthMethodsConfig();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001278
Ed Tanous6c51eab2021-06-03 12:30:29 -07001279 asyncResp->res.jsonValue = {
1280 {"@odata.id", "/redfish/v1/AccountService"},
1281 {"@odata.type", "#AccountService."
1282 "v1_5_0.AccountService"},
1283 {"Id", "AccountService"},
1284 {"Name", "Account Service"},
1285 {"Description", "Account Service"},
1286 {"ServiceEnabled", true},
1287 {"MaxPasswordLength", 20},
1288 {"Accounts",
1289 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
1290 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
1291 {"Oem",
1292 {{"OpenBMC",
1293 {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
Ed Tanousd8e3c292021-09-21 18:01:43 -07001294 {"@odata.id", "/redfish/v1/AccountService#/Oem/OpenBMC"},
Ed Tanous6c51eab2021-06-03 12:30:29 -07001295 {"AuthMethods",
1296 {
1297 {"BasicAuth", authMethodsConfig.basic},
1298 {"SessionToken", authMethodsConfig.sessionToken},
1299 {"XToken", authMethodsConfig.xtoken},
1300 {"Cookie", authMethodsConfig.cookie},
1301 {"TLS", authMethodsConfig.tls},
Abhishek Patel72048782021-06-02 09:53:24 -05001302 }}}}}}};
1303 // /redfish/v1/AccountService/LDAP/Certificates is something only
1304 // ConfigureManager can access then only display when the user has
1305 // permissions ConfigureManager
1306 Privileges effectiveUserPrivileges =
1307 redfish::getUserPrivileges(req.userRole);
1308
1309 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1310 effectiveUserPrivileges))
1311 {
1312 asyncResp->res.jsonValue["LDAP"] = {
1313 {"Certificates",
1314 {{"@odata.id",
1315 "/redfish/v1/AccountService/LDAP/Certificates"}}}};
1316 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001317 crow::connections::systemBus->async_method_call(
1318 [asyncResp](
1319 const boost::system::error_code ec,
1320 const std::vector<
Ed Tanous168e20c2021-12-13 14:39:53 -08001321 std::pair<std::string, dbus::utility::DbusVariantType>>&
Ed Tanous6c51eab2021-06-03 12:30:29 -07001322 propertiesList) {
1323 if (ec)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001324 {
1325 messages::internalError(asyncResp->res);
1326 return;
1327 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001328 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1329 << "properties for AccountService";
1330 for (const std::pair<std::string,
Ed Tanous168e20c2021-12-13 14:39:53 -08001331 dbus::utility::DbusVariantType>&
1332 property : propertiesList)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001333 {
1334 if (property.first == "MinPasswordLength")
1335 {
1336 const uint8_t* value =
1337 std::get_if<uint8_t>(&property.second);
1338 if (value != nullptr)
Ratan Gupta24c85422019-01-30 19:41:24 +05301339 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001340 asyncResp->res.jsonValue["MinPasswordLength"] =
1341 *value;
Ratan Gupta24c85422019-01-30 19:41:24 +05301342 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001343 }
1344 if (property.first == "AccountUnlockTimeout")
1345 {
1346 const uint32_t* value =
1347 std::get_if<uint32_t>(&property.second);
1348 if (value != nullptr)
1349 {
1350 asyncResp->res
1351 .jsonValue["AccountLockoutDuration"] =
1352 *value;
1353 }
1354 }
1355 if (property.first == "MaxLoginAttemptBeforeLockout")
1356 {
1357 const uint16_t* value =
1358 std::get_if<uint16_t>(&property.second);
1359 if (value != nullptr)
1360 {
1361 asyncResp->res
1362 .jsonValue["AccountLockoutThreshold"] =
1363 *value;
1364 }
1365 }
1366 }
1367 },
1368 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1369 "org.freedesktop.DBus.Properties", "GetAll",
1370 "xyz.openbmc_project.User.AccountPolicy");
1371
1372 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
1373 const std::string& ldapType) {
1374 if (!success)
1375 {
1376 return;
1377 }
1378 parseLDAPConfigData(asyncResp->res.jsonValue, confData,
1379 ldapType);
1380 };
1381
1382 getLDAPConfigData("LDAP", callback);
1383 getLDAPConfigData("ActiveDirectory", callback);
1384 });
1385
Ed Tanousf5ffd802021-07-19 10:55:33 -07001386 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
1387 .privileges(redfish::privileges::getAccountService)
1388 .methods(boost::beast::http::verb::patch)(
1389 [](const crow::Request& req,
1390 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1391 std::optional<uint32_t> unlockTimeout;
1392 std::optional<uint16_t> lockoutThreshold;
1393 std::optional<uint16_t> minPasswordLength;
1394 std::optional<uint16_t> maxPasswordLength;
1395 std::optional<nlohmann::json> ldapObject;
1396 std::optional<nlohmann::json> activeDirectoryObject;
1397 std::optional<nlohmann::json> oemObject;
1398
1399 if (!json_util::readJson(
1400 req, asyncResp->res, "AccountLockoutDuration",
1401 unlockTimeout, "AccountLockoutThreshold",
1402 lockoutThreshold, "MaxPasswordLength",
1403 maxPasswordLength, "MinPasswordLength",
1404 minPasswordLength, "LDAP", ldapObject,
1405 "ActiveDirectory", activeDirectoryObject, "Oem",
1406 oemObject))
1407 {
1408 return;
1409 }
1410
1411 if (minPasswordLength)
1412 {
1413 messages::propertyNotWritable(asyncResp->res,
1414 "MinPasswordLength");
1415 }
1416
1417 if (maxPasswordLength)
1418 {
1419 messages::propertyNotWritable(asyncResp->res,
1420 "MaxPasswordLength");
1421 }
1422
1423 if (ldapObject)
1424 {
1425 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1426 }
1427
1428 if (std::optional<nlohmann::json> oemOpenBMCObject;
1429 oemObject &&
1430 json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1431 oemOpenBMCObject))
1432 {
1433 if (std::optional<nlohmann::json> authMethodsObject;
1434 oemOpenBMCObject &&
1435 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1436 "AuthMethods", authMethodsObject))
1437 {
1438 if (authMethodsObject)
1439 {
1440 handleAuthMethodsPatch(*authMethodsObject,
1441 asyncResp);
1442 }
1443 }
1444 }
1445
1446 if (activeDirectoryObject)
1447 {
1448 handleLDAPPatch(*activeDirectoryObject, asyncResp,
1449 "ActiveDirectory");
1450 }
1451
1452 if (unlockTimeout)
1453 {
1454 crow::connections::systemBus->async_method_call(
1455 [asyncResp](const boost::system::error_code ec) {
1456 if (ec)
1457 {
1458 messages::internalError(asyncResp->res);
1459 return;
1460 }
1461 messages::success(asyncResp->res);
1462 },
1463 "xyz.openbmc_project.User.Manager",
1464 "/xyz/openbmc_project/user",
1465 "org.freedesktop.DBus.Properties", "Set",
1466 "xyz.openbmc_project.User.AccountPolicy",
1467 "AccountUnlockTimeout",
Ed Tanous168e20c2021-12-13 14:39:53 -08001468 dbus::utility::DbusVariantType(*unlockTimeout));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001469 }
1470 if (lockoutThreshold)
1471 {
1472 crow::connections::systemBus->async_method_call(
1473 [asyncResp](const boost::system::error_code ec) {
1474 if (ec)
1475 {
1476 messages::internalError(asyncResp->res);
1477 return;
1478 }
1479 messages::success(asyncResp->res);
1480 },
1481 "xyz.openbmc_project.User.Manager",
1482 "/xyz/openbmc_project/user",
1483 "org.freedesktop.DBus.Properties", "Set",
1484 "xyz.openbmc_project.User.AccountPolicy",
1485 "MaxLoginAttemptBeforeLockout",
Ed Tanous168e20c2021-12-13 14:39:53 -08001486 dbus::utility::DbusVariantType(*lockoutThreshold));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001487 }
1488 });
1489
Ed Tanous6c51eab2021-06-03 12:30:29 -07001490 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001491 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001492 .methods(boost::beast::http::verb::get)(
1493 [](const crow::Request& req,
1494 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1495 asyncResp->res.jsonValue = {
1496 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
1497 {"@odata.type", "#ManagerAccountCollection."
1498 "ManagerAccountCollection"},
1499 {"Name", "Accounts Collection"},
1500 {"Description", "BMC User Accounts"}};
1501
Ed Tanous6c51eab2021-06-03 12:30:29 -07001502 Privileges effectiveUserPrivileges =
1503 redfish::getUserPrivileges(req.userRole);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001504
JunLin Chenf5e29f32021-12-08 16:47:04 +08001505 std::string thisUser;
1506 if (req.session)
1507 {
1508 thisUser = req.session->username;
1509 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001510 crow::connections::systemBus->async_method_call(
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001511 [asyncResp, thisUser, effectiveUserPrivileges](
1512 const boost::system::error_code ec,
1513 const ManagedObjectType& users) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001514 if (ec)
1515 {
1516 messages::internalError(asyncResp->res);
Ratan Gupta24c85422019-01-30 19:41:24 +05301517 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001518 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001519
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001520 bool userCanSeeAllAccounts =
1521 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001522 {"ConfigureUsers"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001523
1524 bool userCanSeeSelf =
1525 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001526 {"ConfigureSelf"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001527
Ed Tanous6c51eab2021-06-03 12:30:29 -07001528 nlohmann::json& memberArray =
1529 asyncResp->res.jsonValue["Members"];
1530 memberArray = nlohmann::json::array();
Ratan Gupta24c85422019-01-30 19:41:24 +05301531
Ed Tanous6c51eab2021-06-03 12:30:29 -07001532 for (auto& userpath : users)
1533 {
1534 std::string user = userpath.first.filename();
1535 if (user.empty())
Ratan Gupta24c85422019-01-30 19:41:24 +05301536 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301537 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001538 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1539
Ratan Gupta24c85422019-01-30 19:41:24 +05301540 return;
1541 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301542
Ed Tanous6c51eab2021-06-03 12:30:29 -07001543 // As clarified by Redfish here:
1544 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1545 // Users without ConfigureUsers, only see their own
1546 // account. Users with ConfigureUsers, see all
1547 // accounts.
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001548 if (userCanSeeAllAccounts ||
1549 (thisUser == user && userCanSeeSelf))
Ratan Gupta24c85422019-01-30 19:41:24 +05301550 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001551 memberArray.push_back(
1552 {{"@odata.id",
1553 "/redfish/v1/AccountService/Accounts/" +
1554 user}});
Ratan Gupta24c85422019-01-30 19:41:24 +05301555 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001556 }
1557 asyncResp->res.jsonValue["Members@odata.count"] =
1558 memberArray.size();
1559 },
1560 "xyz.openbmc_project.User.Manager",
1561 "/xyz/openbmc_project/user",
1562 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Ratan Gupta24c85422019-01-30 19:41:24 +05301563 });
Ed Tanous06e086d2018-09-19 17:19:52 -07001564
Ed Tanous6c51eab2021-06-03 12:30:29 -07001565 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001566 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001567 .methods(boost::beast::http::verb::post)([](const crow::Request& req,
1568 const std::shared_ptr<
1569 bmcweb::AsyncResp>&
1570 asyncResp) -> void {
1571 std::string username;
1572 std::string password;
1573 std::optional<std::string> roleId("User");
1574 std::optional<bool> enabled = true;
1575 if (!json_util::readJson(req, asyncResp->res, "UserName", username,
1576 "Password", password, "RoleId", roleId,
1577 "Enabled", enabled))
1578 {
1579 return;
1580 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001581
Ed Tanous6c51eab2021-06-03 12:30:29 -07001582 std::string priv = getPrivilegeFromRoleId(*roleId);
1583 if (priv.empty())
1584 {
1585 messages::propertyValueNotInList(asyncResp->res, *roleId,
1586 "RoleId");
1587 return;
1588 }
1589 // TODO: Following override will be reverted once support in
1590 // phosphor-user-manager is added. In order to avoid dependency
1591 // issues, this is added in bmcweb, which will removed, once
1592 // phosphor-user-manager supports priv-noaccess.
1593 if (priv == "priv-noaccess")
1594 {
1595 roleId = "";
1596 }
1597 else
1598 {
1599 roleId = priv;
1600 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001601
Ed Tanous6c51eab2021-06-03 12:30:29 -07001602 // Reading AllGroups property
1603 crow::connections::systemBus->async_method_call(
1604 [asyncResp, username, password{std::move(password)}, roleId,
Ed Tanous168e20c2021-12-13 14:39:53 -08001605 enabled](const boost::system::error_code ec,
1606 const dbus::utility::DbusVariantType& allGroups) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001607 if (ec)
1608 {
1609 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1610 messages::internalError(asyncResp->res);
1611 return;
1612 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001613
Ed Tanous6c51eab2021-06-03 12:30:29 -07001614 const std::vector<std::string>* allGroupsList =
1615 std::get_if<std::vector<std::string>>(&allGroups);
1616
1617 if (allGroupsList == nullptr || allGroupsList->empty())
1618 {
1619 messages::internalError(asyncResp->res);
1620 return;
1621 }
1622
1623 crow::connections::systemBus->async_method_call(
1624 [asyncResp, username,
1625 password](const boost::system::error_code ec2,
1626 sdbusplus::message::message& m) {
1627 if (ec2)
1628 {
1629 userErrorMessageHandler(
1630 m.get_error(), asyncResp, username, "");
1631 return;
1632 }
1633
1634 if (pamUpdatePassword(username, password) !=
1635 PAM_SUCCESS)
1636 {
1637 // At this point we have a user that's been
1638 // created, but the password set
1639 // failed.Something is wrong, so delete the user
1640 // that we've already created
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301641 sdbusplus::message::object_path tempObjPath(
1642 rootUserDbusPath);
1643 tempObjPath /= username;
1644 const std::string userPath(tempObjPath);
1645
Ed Tanous6c51eab2021-06-03 12:30:29 -07001646 crow::connections::systemBus->async_method_call(
1647 [asyncResp, password](
1648 const boost::system::error_code ec3) {
1649 if (ec3)
1650 {
1651 messages::internalError(
1652 asyncResp->res);
1653 return;
1654 }
1655
1656 // If password is invalid
1657 messages::propertyValueFormatError(
1658 asyncResp->res, password,
1659 "Password");
1660 },
1661 "xyz.openbmc_project.User.Manager",
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301662 userPath,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001663 "xyz.openbmc_project.Object.Delete",
1664 "Delete");
1665
1666 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1667 return;
1668 }
1669
1670 messages::created(asyncResp->res);
1671 asyncResp->res.addHeader(
1672 "Location",
1673 "/redfish/v1/AccountService/Accounts/" +
1674 username);
1675 },
1676 "xyz.openbmc_project.User.Manager",
1677 "/xyz/openbmc_project/user",
1678 "xyz.openbmc_project.User.Manager", "CreateUser",
1679 username, *allGroupsList, *roleId, *enabled);
1680 },
1681 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1682 "org.freedesktop.DBus.Properties", "Get",
1683 "xyz.openbmc_project.User.Manager", "AllGroups");
1684 });
1685
1686 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001687 .privileges(redfish::privileges::getManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001688 .methods(
1689 boost::beast::http::verb::
1690 get)([](const crow::Request& req,
1691 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1692 const std::string& accountName) -> void {
1693 if (req.session->username != accountName)
1694 {
1695 // At this point we've determined that the user is trying to
1696 // modify a user that isn't them. We need to verify that they
1697 // have permissions to modify other users, so re-run the auth
1698 // check with the same permissions, minus ConfigureSelf.
1699 Privileges effectiveUserPrivileges =
1700 redfish::getUserPrivileges(req.userRole);
1701 Privileges requiredPermissionsToChangeNonSelf = {
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001702 "ConfigureUsers", "ConfigureManager"};
Ed Tanous6c51eab2021-06-03 12:30:29 -07001703 if (!effectiveUserPrivileges.isSupersetOf(
1704 requiredPermissionsToChangeNonSelf))
Ed Tanous06e086d2018-09-19 17:19:52 -07001705 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001706 BMCWEB_LOG_DEBUG << "GET Account denied access";
1707 messages::insufficientPrivilege(asyncResp->res);
1708 return;
1709 }
1710 }
1711
1712 crow::connections::systemBus->async_method_call(
1713 [asyncResp, accountName](const boost::system::error_code ec,
1714 const ManagedObjectType& users) {
1715 if (ec)
1716 {
1717 messages::internalError(asyncResp->res);
1718 return;
1719 }
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301720 auto userIt = std::find_if(
1721 users.begin(), users.end(),
1722 [accountName](
1723 const std::pair<sdbusplus::message::object_path,
1724 DbusInterfaceType>& user) {
1725 return !accountName.compare(user.first.filename());
1726 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001727
Ed Tanous6c51eab2021-06-03 12:30:29 -07001728 if (userIt == users.end())
1729 {
1730 messages::resourceNotFound(
1731 asyncResp->res, "ManagerAccount", accountName);
1732 return;
1733 }
1734
1735 asyncResp->res.jsonValue = {
1736 {"@odata.type",
1737 "#ManagerAccount.v1_4_0.ManagerAccount"},
1738 {"Name", "User Account"},
1739 {"Description", "User Account"},
1740 {"Password", nullptr},
1741 {"AccountTypes", {"Redfish"}}};
1742
1743 for (const auto& interface : userIt->second)
1744 {
1745 if (interface.first ==
1746 "xyz.openbmc_project.User.Attributes")
1747 {
1748 for (const auto& property : interface.second)
1749 {
1750 if (property.first == "UserEnabled")
1751 {
1752 const bool* userEnabled =
1753 std::get_if<bool>(&property.second);
1754 if (userEnabled == nullptr)
1755 {
1756 BMCWEB_LOG_ERROR
1757 << "UserEnabled wasn't a bool";
1758 messages::internalError(asyncResp->res);
1759 return;
1760 }
1761 asyncResp->res.jsonValue["Enabled"] =
1762 *userEnabled;
1763 }
1764 else if (property.first ==
1765 "UserLockedForFailedAttempt")
1766 {
1767 const bool* userLocked =
1768 std::get_if<bool>(&property.second);
1769 if (userLocked == nullptr)
1770 {
1771 BMCWEB_LOG_ERROR << "UserLockedForF"
1772 "ailedAttempt "
1773 "wasn't a bool";
1774 messages::internalError(asyncResp->res);
1775 return;
1776 }
1777 asyncResp->res.jsonValue["Locked"] =
1778 *userLocked;
1779 asyncResp->res.jsonValue
1780 ["Locked@Redfish.AllowableValues"] = {
1781 "false"}; // can only unlock accounts
1782 }
1783 else if (property.first == "UserPrivilege")
1784 {
1785 const std::string* userPrivPtr =
1786 std::get_if<std::string>(
1787 &property.second);
1788 if (userPrivPtr == nullptr)
1789 {
1790 BMCWEB_LOG_ERROR
1791 << "UserPrivilege wasn't a "
1792 "string";
1793 messages::internalError(asyncResp->res);
1794 return;
1795 }
1796 std::string role =
1797 getRoleIdFromPrivilege(*userPrivPtr);
1798 if (role.empty())
1799 {
1800 BMCWEB_LOG_ERROR << "Invalid user role";
1801 messages::internalError(asyncResp->res);
1802 return;
1803 }
1804 asyncResp->res.jsonValue["RoleId"] = role;
1805
1806 asyncResp->res.jsonValue["Links"]["Role"] =
1807 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +08001808 "/redfish/v1/AccountService/Roles/" +
Ed Tanous6c51eab2021-06-03 12:30:29 -07001809 role}};
1810 }
1811 else if (property.first ==
1812 "UserPasswordExpired")
1813 {
1814 const bool* userPasswordExpired =
1815 std::get_if<bool>(&property.second);
1816 if (userPasswordExpired == nullptr)
1817 {
George Liu0fda0f12021-11-16 10:06:17 +08001818 BMCWEB_LOG_ERROR
1819 << "UserPasswordExpired wasn't a bool";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001820 messages::internalError(asyncResp->res);
1821 return;
1822 }
1823 asyncResp->res
1824 .jsonValue["PasswordChangeRequired"] =
1825 *userPasswordExpired;
1826 }
1827 }
1828 }
1829 }
1830
1831 asyncResp->res.jsonValue["@odata.id"] =
1832 "/redfish/v1/AccountService/Accounts/" + accountName;
1833 asyncResp->res.jsonValue["Id"] = accountName;
1834 asyncResp->res.jsonValue["UserName"] = accountName;
1835 },
1836 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1837 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1838 });
1839
1840 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001841 // TODO this privilege should be using the generated endpoints, but
1842 // because of the special handling of ConfigureSelf, it's not able to
1843 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001844 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1845 .methods(boost::beast::http::verb::patch)(
1846 [](const crow::Request& req,
1847 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1848 const std::string& username) -> void {
1849 std::optional<std::string> newUserName;
1850 std::optional<std::string> password;
1851 std::optional<bool> enabled;
1852 std::optional<std::string> roleId;
1853 std::optional<bool> locked;
Ed Tanouse9cc5172021-11-03 14:13:19 +08001854
1855 Privileges effectiveUserPrivileges =
1856 redfish::getUserPrivileges(req.userRole);
1857 Privileges configureUsers = {"ConfigureUsers"};
1858 bool userHasConfigureUsers =
1859 effectiveUserPrivileges.isSupersetOf(configureUsers);
1860 if (userHasConfigureUsers)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001861 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001862 // Users with ConfigureUsers can modify for all users
1863 if (!json_util::readJson(req, asyncResp->res, "UserName",
1864 newUserName, "Password", password,
1865 "RoleId", roleId, "Enabled",
1866 enabled, "Locked", locked))
1867 {
1868 return;
1869 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001870 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001871 else
Ed Tanous6c51eab2021-06-03 12:30:29 -07001872 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001873 // ConfigureSelf accounts can only modify their own account
1874 if (username != req.session->username)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001875 {
1876 messages::insufficientPrivilege(asyncResp->res);
1877 return;
1878 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001879 // ConfigureSelf accounts can only modify their password
1880 if (!json_util::readJson(req, asyncResp->res, "Password",
1881 password))
1882 {
1883 return;
1884 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001885 }
1886
1887 // if user name is not provided in the patch method or if it
1888 // matches the user name in the URI, then we are treating it as
1889 // updating user properties other then username. If username
1890 // provided doesn't match the URI, then we are treating this as
1891 // user rename request.
1892 if (!newUserName || (newUserName.value() == username))
1893 {
1894 updateUserProperties(asyncResp, username, password, enabled,
1895 roleId, locked);
1896 return;
1897 }
1898 crow::connections::systemBus->async_method_call(
1899 [asyncResp, username, password(std::move(password)),
1900 roleId(std::move(roleId)), enabled,
1901 newUser{std::string(*newUserName)},
1902 locked](const boost::system::error_code ec,
1903 sdbusplus::message::message& m) {
1904 if (ec)
1905 {
1906 userErrorMessageHandler(m.get_error(), asyncResp,
1907 newUser, username);
1908 return;
1909 }
1910
1911 updateUserProperties(asyncResp, newUser, password,
1912 enabled, roleId, locked);
1913 },
1914 "xyz.openbmc_project.User.Manager",
1915 "/xyz/openbmc_project/user",
1916 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1917 *newUserName);
1918 });
1919
1920 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001921 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001922 .methods(boost::beast::http::verb::delete_)(
1923 [](const crow::Request& /*req*/,
1924 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1925 const std::string& username) -> void {
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301926 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1927 tempObjPath /= username;
1928 const std::string userPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001929
1930 crow::connections::systemBus->async_method_call(
1931 [asyncResp, username](const boost::system::error_code ec) {
1932 if (ec)
1933 {
1934 messages::resourceNotFound(
1935 asyncResp->res,
1936 "#ManagerAccount.v1_4_0.ManagerAccount",
1937 username);
1938 return;
1939 }
1940
1941 messages::accountRemoved(asyncResp->res);
1942 },
1943 "xyz.openbmc_project.User.Manager", userPath,
1944 "xyz.openbmc_project.Object.Delete", "Delete");
1945 });
1946}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001947
Ed Tanous1abe55e2018-09-05 08:30:59 -07001948} // namespace redfish