blob: d59c8c2ba9bcf33f070975e2f6df7283d2a9b312 [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) ||
George Liu0fda0f12021-11-16 10:06:17 +0800149 (strcmp(
150 errorMessage,
151 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
152 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000153 {
154 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
155 }
156 else if (strcmp(errorMessage,
157 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
158 {
159 messages::createLimitReachedForResource(asyncResp->res);
160 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000161 else
162 {
163 messages::internalError(asyncResp->res);
164 }
165
166 return;
167}
168
Ed Tanous81ce6092020-12-17 16:54:55 +0000169inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000170 const LDAPConfigData& confData,
171 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530172{
Ratan Guptaab828d72019-04-22 14:18:33 +0530173 std::string service =
174 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600175 nlohmann::json ldap = {
Ratan Gupta6973a582018-12-13 18:25:44 +0530176 {"ServiceEnabled", confData.serviceEnabled},
177 {"ServiceAddresses", nlohmann::json::array({confData.uri})},
178 {"Authentication",
179 {{"AuthenticationType", "UsernameAndPassword"},
Ratan Gupta6973a582018-12-13 18:25:44 +0530180 {"Username", confData.bindDN},
181 {"Password", nullptr}}},
182 {"LDAPService",
183 {{"SearchSettings",
184 {{"BaseDistinguishedNames",
185 nlohmann::json::array({confData.baseDN})},
186 {"UsernameAttribute", confData.userNameAttribute},
187 {"GroupsAttribute", confData.groupAttribute}}}}},
188 };
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600189
Ed Tanous81ce6092020-12-17 16:54:55 +0000190 jsonResponse[ldapType].update(ldap);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600191
Ed Tanous81ce6092020-12-17 16:54:55 +0000192 nlohmann::json& roleMapArray = jsonResponse[ldapType]["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600193 roleMapArray = nlohmann::json::array();
194 for (auto& obj : confData.groupRoleList)
195 {
196 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
197 << obj.second.groupName << "\n";
198 roleMapArray.push_back(
199 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
200 nlohmann::json::array(
201 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
202 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530203}
204
205/**
Ratan Gupta06785242019-07-26 22:30:16 +0530206 * @brief validates given JSON input and then calls appropriate method to
207 * create, to delete or to set Rolemapping object based on the given input.
208 *
209 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000210inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800211 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530212 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700213 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530214{
215 for (size_t index = 0; index < input.size(); index++)
216 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700217 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530218
219 if (thisJson.is_null())
220 {
221 // delete the existing object
222 if (index < roleMapObjData.size())
223 {
224 crow::connections::systemBus->async_method_call(
225 [asyncResp, roleMapObjData, serverType,
226 index](const boost::system::error_code ec) {
227 if (ec)
228 {
229 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
230 messages::internalError(asyncResp->res);
231 return;
232 }
233 asyncResp->res
234 .jsonValue[serverType]["RemoteRoleMapping"][index] =
235 nullptr;
236 },
237 ldapDbusService, roleMapObjData[index].first,
238 "xyz.openbmc_project.Object.Delete", "Delete");
239 }
240 else
241 {
242 BMCWEB_LOG_ERROR << "Can't delete the object";
243 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800244 asyncResp->res,
245 thisJson.dump(2, ' ', true,
246 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530247 "RemoteRoleMapping/" + std::to_string(index));
248 return;
249 }
250 }
251 else if (thisJson.empty())
252 {
253 // Don't do anything for the empty objects,parse next json
254 // eg {"RemoteRoleMapping",[{}]}
255 }
256 else
257 {
258 // update/create the object
259 std::optional<std::string> remoteGroup;
260 std::optional<std::string> localRole;
261
Ed Tanousf23b7292020-10-15 09:41:17 -0700262 // This is a copy, but it's required in this case because of how
263 // readJson is structured
264 nlohmann::json thisJsonCopy = thisJson;
265 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
266 "RemoteGroup", remoteGroup, "LocalRole",
267 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530268 {
269 continue;
270 }
271
272 // Update existing RoleMapping Object
273 if (index < roleMapObjData.size())
274 {
275 BMCWEB_LOG_DEBUG << "Update Role Map Object";
276 // If "RemoteGroup" info is provided
277 if (remoteGroup)
278 {
279 crow::connections::systemBus->async_method_call(
280 [asyncResp, roleMapObjData, serverType, index,
281 remoteGroup](const boost::system::error_code ec) {
282 if (ec)
283 {
284 BMCWEB_LOG_ERROR << "DBUS response error: "
285 << ec;
286 messages::internalError(asyncResp->res);
287 return;
288 }
289 asyncResp->res
290 .jsonValue[serverType]["RemoteRoleMapping"]
291 [index]["RemoteGroup"] = *remoteGroup;
292 },
293 ldapDbusService, roleMapObjData[index].first,
294 propertyInterface, "Set",
295 "xyz.openbmc_project.User.PrivilegeMapperEntry",
296 "GroupName",
297 std::variant<std::string>(std::move(*remoteGroup)));
298 }
299
300 // If "LocalRole" info is provided
301 if (localRole)
302 {
303 crow::connections::systemBus->async_method_call(
304 [asyncResp, roleMapObjData, serverType, index,
305 localRole](const boost::system::error_code ec) {
306 if (ec)
307 {
308 BMCWEB_LOG_ERROR << "DBUS response error: "
309 << ec;
310 messages::internalError(asyncResp->res);
311 return;
312 }
313 asyncResp->res
314 .jsonValue[serverType]["RemoteRoleMapping"]
315 [index]["LocalRole"] = *localRole;
316 },
317 ldapDbusService, roleMapObjData[index].first,
318 propertyInterface, "Set",
319 "xyz.openbmc_project.User.PrivilegeMapperEntry",
320 "Privilege",
321 std::variant<std::string>(
322 getPrivilegeFromRoleId(std::move(*localRole))));
323 }
324 }
325 // Create a new RoleMapping Object.
326 else
327 {
328 BMCWEB_LOG_DEBUG
329 << "setRoleMappingProperties: Creating new Object";
330 std::string pathString =
331 "RemoteRoleMapping/" + std::to_string(index);
332
333 if (!localRole)
334 {
335 messages::propertyMissing(asyncResp->res,
336 pathString + "/LocalRole");
337 continue;
338 }
339 if (!remoteGroup)
340 {
341 messages::propertyMissing(asyncResp->res,
342 pathString + "/RemoteGroup");
343 continue;
344 }
345
346 std::string dbusObjectPath;
347 if (serverType == "ActiveDirectory")
348 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700349 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530350 }
351 else if (serverType == "LDAP")
352 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000353 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530354 }
355
356 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
357 << ",LocalRole=" << *localRole;
358
359 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700360 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530361 remoteGroup](const boost::system::error_code ec) {
362 if (ec)
363 {
364 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
365 messages::internalError(asyncResp->res);
366 return;
367 }
368 nlohmann::json& remoteRoleJson =
369 asyncResp->res
370 .jsonValue[serverType]["RemoteRoleMapping"];
371 remoteRoleJson.push_back(
372 {{"LocalRole", *localRole},
373 {"RemoteGroup", *remoteGroup}});
374 },
375 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700376 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530377 getPrivilegeFromRoleId(std::move(*localRole)));
378 }
379 }
380 }
381}
382
383/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530384 * Function that retrieves all properties for LDAP config object
385 * into JSON
386 */
387template <typename CallbackFunc>
388inline void getLDAPConfigData(const std::string& ldapType,
389 CallbackFunc&& callback)
390{
Ratan Guptaab828d72019-04-22 14:18:33 +0530391
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600392 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530393 ldapConfigInterface};
394
395 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600396 [callback, ldapType](const boost::system::error_code ec,
397 const GetObjectType& resp) {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600398 if (ec || resp.empty())
399 {
George Liu0fda0f12021-11-16 10:06:17 +0800400 BMCWEB_LOG_ERROR
401 << "DBUS response error during getting of service name: "
402 << ec;
Ed Tanous23a21a12020-07-25 04:45:05 +0000403 LDAPConfigData empty{};
404 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600405 return;
406 }
407 std::string service = resp.begin()->first;
408 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000409 [callback, ldapType](const boost::system::error_code errorCode,
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600410 const ManagedObjectType& ldapObjects) {
411 LDAPConfigData confData{};
Ed Tanous81ce6092020-12-17 16:54:55 +0000412 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600413 {
414 callback(false, confData, ldapType);
415 BMCWEB_LOG_ERROR << "D-Bus responses error: "
Ed Tanous81ce6092020-12-17 16:54:55 +0000416 << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600417 return;
418 }
419
420 std::string ldapDbusType;
421 std::string searchString;
422
423 if (ldapType == "LDAP")
424 {
George Liu0fda0f12021-11-16 10:06:17 +0800425 ldapDbusType =
426 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600427 searchString = "openldap";
428 }
429 else if (ldapType == "ActiveDirectory")
430 {
431 ldapDbusType =
George Liu0fda0f12021-11-16 10:06:17 +0800432 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600433 searchString = "active_directory";
434 }
435 else
436 {
437 BMCWEB_LOG_ERROR
438 << "Can't get the DbusType for the given type="
439 << ldapType;
440 callback(false, confData, ldapType);
441 return;
442 }
443
444 std::string ldapEnableInterfaceStr = ldapEnableInterface;
445 std::string ldapConfigInterfaceStr = ldapConfigInterface;
446
447 for (const auto& object : ldapObjects)
448 {
449 // let's find the object whose ldap type is equal to the
450 // given type
451 if (object.first.str.find(searchString) ==
452 std::string::npos)
453 {
454 continue;
455 }
456
457 for (const auto& interface : object.second)
458 {
459 if (interface.first == ldapEnableInterfaceStr)
460 {
461 // rest of the properties are string.
462 for (const auto& property : interface.second)
463 {
464 if (property.first == "Enabled")
465 {
466 const bool* value =
467 std::get_if<bool>(&property.second);
468 if (value == nullptr)
469 {
470 continue;
471 }
472 confData.serviceEnabled = *value;
473 break;
474 }
475 }
476 }
477 else if (interface.first == ldapConfigInterfaceStr)
478 {
479
480 for (const auto& property : interface.second)
481 {
Ed Tanous271584a2019-07-09 16:24:22 -0700482 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600483 std::get_if<std::string>(
484 &property.second);
Ed Tanous271584a2019-07-09 16:24:22 -0700485 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600486 {
487 continue;
488 }
489 if (property.first == "LDAPServerURI")
490 {
Ed Tanous271584a2019-07-09 16:24:22 -0700491 confData.uri = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600492 }
493 else if (property.first == "LDAPBindDN")
494 {
Ed Tanous271584a2019-07-09 16:24:22 -0700495 confData.bindDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600496 }
497 else if (property.first == "LDAPBaseDN")
498 {
Ed Tanous271584a2019-07-09 16:24:22 -0700499 confData.baseDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600500 }
501 else if (property.first ==
502 "LDAPSearchScope")
503 {
Ed Tanous271584a2019-07-09 16:24:22 -0700504 confData.searchScope = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600505 }
506 else if (property.first ==
507 "GroupNameAttribute")
508 {
Ed Tanous271584a2019-07-09 16:24:22 -0700509 confData.groupAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600510 }
511 else if (property.first ==
512 "UserNameAttribute")
513 {
Ed Tanous271584a2019-07-09 16:24:22 -0700514 confData.userNameAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600515 }
516 else if (property.first == "LDAPType")
517 {
Ed Tanous271584a2019-07-09 16:24:22 -0700518 confData.serverType = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600519 }
520 }
521 }
George Liu0fda0f12021-11-16 10:06:17 +0800522 else if (
523 interface.first ==
524 "xyz.openbmc_project.User.PrivilegeMapperEntry")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600525 {
526 LDAPRoleMapData roleMapData{};
527 for (const auto& property : interface.second)
528 {
Ed Tanous271584a2019-07-09 16:24:22 -0700529 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600530 std::get_if<std::string>(
531 &property.second);
532
Ed Tanous271584a2019-07-09 16:24:22 -0700533 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600534 {
535 continue;
536 }
537
538 if (property.first == "GroupName")
539 {
Ed Tanous271584a2019-07-09 16:24:22 -0700540 roleMapData.groupName = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600541 }
542 else if (property.first == "Privilege")
543 {
Ed Tanous271584a2019-07-09 16:24:22 -0700544 roleMapData.privilege = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600545 }
546 }
547
Ed Tanous0f0353b2019-10-24 11:37:51 -0700548 confData.groupRoleList.emplace_back(
549 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600550 }
551 }
552 }
553 callback(true, confData, ldapType);
554 },
555 service, ldapRootObject, dbusObjManagerIntf,
556 "GetManagedObjects");
557 },
558 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
Ed Tanous23a21a12020-07-25 04:45:05 +0000559 ldapConfigObjectName, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530560}
561
Ed Tanous6c51eab2021-06-03 12:30:29 -0700562/**
563 * @brief parses the authentication section under the LDAP
564 * @param input JSON data
565 * @param asyncResp pointer to the JSON response
566 * @param userName userName to be filled from the given JSON.
567 * @param password password to be filled from the given JSON.
568 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700569inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700570 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
571 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700573 std::optional<std::string> authType;
574
575 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
576 authType, "Username", username, "Password",
577 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700579 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700581 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530582 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700583 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530584 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700585 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530586 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700587 messages::propertyValueNotInList(asyncResp->res, *authType,
588 "AuthenticationType");
589 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530590 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700591}
592/**
593 * @brief parses the LDAPService section under the LDAP
594 * @param input JSON data
595 * @param asyncResp pointer to the JSON response
596 * @param baseDNList baseDN to be filled from the given JSON.
597 * @param userNameAttribute userName to be filled from the given JSON.
598 * @param groupaAttribute password to be filled from the given JSON.
599 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530600
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700601inline void
602 parseLDAPServiceJson(nlohmann::json input,
603 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
604 std::optional<std::vector<std::string>>& baseDNList,
605 std::optional<std::string>& userNameAttribute,
606 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700607{
608 std::optional<nlohmann::json> searchSettings;
609
610 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
611 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530612 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700613 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530614 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700615 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530616 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700617 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530618 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700619 if (!json_util::readJson(*searchSettings, asyncResp->res,
620 "BaseDistinguishedNames", baseDNList,
621 "UsernameAttribute", userNameAttribute,
622 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530623 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700624 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530625 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700626}
627/**
628 * @brief updates the LDAP server address and updates the
629 json response with the new value.
630 * @param serviceAddressList address to be updated.
631 * @param asyncResp pointer to the JSON response
632 * @param ldapServerElementName Type of LDAP
633 server(openLDAP/ActiveDirectory)
634 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530635
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700636inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700637 const std::vector<std::string>& serviceAddressList,
638 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
639 const std::string& ldapServerElementName,
640 const std::string& ldapConfigObject)
641{
642 crow::connections::systemBus->async_method_call(
643 [asyncResp, ldapServerElementName,
644 serviceAddressList](const boost::system::error_code ec) {
645 if (ec)
646 {
647 BMCWEB_LOG_DEBUG
648 << "Error Occurred in updating the service address";
649 messages::internalError(asyncResp->res);
650 return;
651 }
652 std::vector<std::string> modifiedserviceAddressList = {
653 serviceAddressList.front()};
654 asyncResp->res
655 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
656 modifiedserviceAddressList;
657 if ((serviceAddressList).size() > 1)
658 {
659 messages::propertyValueModified(asyncResp->res,
660 "ServiceAddresses",
661 serviceAddressList.front());
662 }
663 BMCWEB_LOG_DEBUG << "Updated the service address";
664 },
665 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
666 ldapConfigInterface, "LDAPServerURI",
667 std::variant<std::string>(serviceAddressList.front()));
668}
669/**
670 * @brief updates the LDAP Bind DN and updates the
671 json response with the new value.
672 * @param username name of the user which needs to be updated.
673 * @param asyncResp pointer to the JSON response
674 * @param ldapServerElementName Type of LDAP
675 server(openLDAP/ActiveDirectory)
676 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530677
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700678inline void
679 handleUserNamePatch(const std::string& username,
680 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
681 const std::string& ldapServerElementName,
682 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700683{
684 crow::connections::systemBus->async_method_call(
685 [asyncResp, username,
686 ldapServerElementName](const boost::system::error_code ec) {
687 if (ec)
688 {
689 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
690 messages::internalError(asyncResp->res);
691 return;
692 }
693 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
694 ["Username"] = username;
695 BMCWEB_LOG_DEBUG << "Updated the username";
696 },
697 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
698 ldapConfigInterface, "LDAPBindDN", std::variant<std::string>(username));
699}
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",
730 std::variant<std::string>(password));
731}
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",
773 std::variant<std::string>(baseDNList.front()));
774}
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",
809 std::variant<std::string>(userNameAttribute));
810}
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",
845 std::variant<std::string>(groupsAttribute));
846}
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",
876 ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
877}
878
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700879inline void
880 handleAuthMethodsPatch(nlohmann::json& input,
881 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700882{
883 std::optional<bool> basicAuth;
884 std::optional<bool> cookie;
885 std::optional<bool> sessionToken;
886 std::optional<bool> xToken;
887 std::optional<bool> tls;
888
889 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
890 "Cookie", cookie, "SessionToken", sessionToken,
891 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530892 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700893 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
894 return;
895 }
896
897 // Make a copy of methods configuration
898 persistent_data::AuthConfigMethods authMethodsConfig =
899 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
900
901 if (basicAuth)
902 {
903#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
904 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800905 asyncResp->res,
906 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700907 return;
908#endif
909 authMethodsConfig.basic = *basicAuth;
910 }
911
912 if (cookie)
913 {
914#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800915 messages::actionNotSupported(
916 asyncResp->res,
917 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700918 return;
919#endif
920 authMethodsConfig.cookie = *cookie;
921 }
922
923 if (sessionToken)
924 {
925#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
926 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800927 asyncResp->res,
928 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700929 return;
930#endif
931 authMethodsConfig.sessionToken = *sessionToken;
932 }
933
934 if (xToken)
935 {
936#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800937 messages::actionNotSupported(
938 asyncResp->res,
939 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700940 return;
941#endif
942 authMethodsConfig.xtoken = *xToken;
943 }
944
945 if (tls)
946 {
947#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800948 messages::actionNotSupported(
949 asyncResp->res,
950 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700951 return;
952#endif
953 authMethodsConfig.tls = *tls;
954 }
955
956 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
957 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
958 !authMethodsConfig.tls)
959 {
960 // Do not allow user to disable everything
961 messages::actionNotSupported(asyncResp->res,
962 "of disabling all available methods");
963 return;
964 }
965
966 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
967 authMethodsConfig);
968 // Save configuration immediately
969 persistent_data::getConfig().writeData();
970
971 messages::success(asyncResp->res);
972}
973
974/**
975 * @brief Get the required values from the given JSON, validates the
976 * value and create the LDAP config object.
977 * @param input JSON data
978 * @param asyncResp pointer to the JSON response
979 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
980 */
981
982inline void handleLDAPPatch(nlohmann::json& input,
983 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
984 const std::string& serverType)
985{
986 std::string dbusObjectPath;
987 if (serverType == "ActiveDirectory")
988 {
989 dbusObjectPath = adConfigObject;
990 }
991 else if (serverType == "LDAP")
992 {
993 dbusObjectPath = ldapConfigObjectName;
994 }
995 else
996 {
997 return;
998 }
999
1000 std::optional<nlohmann::json> authentication;
1001 std::optional<nlohmann::json> ldapService;
1002 std::optional<std::vector<std::string>> serviceAddressList;
1003 std::optional<bool> serviceEnabled;
1004 std::optional<std::vector<std::string>> baseDNList;
1005 std::optional<std::string> userNameAttribute;
1006 std::optional<std::string> groupsAttribute;
1007 std::optional<std::string> userName;
1008 std::optional<std::string> password;
1009 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1010
1011 if (!json_util::readJson(input, asyncResp->res, "Authentication",
1012 authentication, "LDAPService", ldapService,
1013 "ServiceAddresses", serviceAddressList,
1014 "ServiceEnabled", serviceEnabled,
1015 "RemoteRoleMapping", remoteRoleMapData))
1016 {
1017 return;
1018 }
1019
1020 if (authentication)
1021 {
1022 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1023 password);
1024 }
1025 if (ldapService)
1026 {
1027 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1028 userNameAttribute, groupsAttribute);
1029 }
1030 if (serviceAddressList)
1031 {
1032 if ((*serviceAddressList).size() == 0)
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301033 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001034 messages::propertyValueNotInList(asyncResp->res, "[]",
1035 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001036 return;
1037 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001038 }
1039 if (baseDNList)
1040 {
1041 if ((*baseDNList).size() == 0)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301042 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001043 messages::propertyValueNotInList(asyncResp->res, "[]",
1044 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301045 return;
1046 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001047 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301048
Ed Tanous6c51eab2021-06-03 12:30:29 -07001049 // nothing to update, then return
1050 if (!userName && !password && !serviceAddressList && !baseDNList &&
1051 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1052 !remoteRoleMapData)
1053 {
1054 return;
1055 }
1056
1057 // Get the existing resource first then keep modifying
1058 // whenever any property gets updated.
1059 getLDAPConfigData(serverType, [asyncResp, userName, password, baseDNList,
1060 userNameAttribute, groupsAttribute,
1061 serviceAddressList, serviceEnabled,
1062 dbusObjectPath, remoteRoleMapData](
1063 bool success,
1064 const LDAPConfigData& confData,
1065 const std::string& serverT) {
1066 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301067 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001068 messages::internalError(asyncResp->res);
1069 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301070 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001071 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1072 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301073 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001074 // Disable the service first and update the rest of
1075 // the properties.
1076 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301077 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001078
Ratan Gupta8a07d282019-03-16 08:33:47 +05301079 if (serviceAddressList)
1080 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001081 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1082 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301083 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001084 if (userName)
1085 {
1086 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1087 }
1088 if (password)
1089 {
1090 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1091 }
1092
Ratan Gupta8a07d282019-03-16 08:33:47 +05301093 if (baseDNList)
1094 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001095 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1096 }
1097 if (userNameAttribute)
1098 {
1099 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1100 dbusObjectPath);
1101 }
1102 if (groupsAttribute)
1103 {
1104 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1105 dbusObjectPath);
1106 }
1107 if (serviceEnabled)
1108 {
1109 // if user has given the value as true then enable
1110 // the service. if user has given false then no-op
1111 // as service is already stopped.
1112 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301113 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001114 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1115 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301116 }
1117 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001118 else
1119 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001120 // if user has not given the service enabled value
1121 // then revert it to the same state as it was
1122 // before.
1123 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1124 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001125 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001126
Ed Tanous6c51eab2021-06-03 12:30:29 -07001127 if (remoteRoleMapData)
1128 {
1129 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1130 *remoteRoleMapData);
1131 }
1132 });
1133}
1134
1135inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1136 const std::string& username,
1137 std::optional<std::string> password,
1138 std::optional<bool> enabled,
1139 std::optional<std::string> roleId,
1140 std::optional<bool> locked)
1141{
1142 std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
1143 dbus::utility::escapePathForDbus(dbusObjectPath);
1144
1145 dbus::utility::checkDbusPathExists(
1146 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001147 [dbusObjectPath, username, password(std::move(password)),
1148 roleId(std::move(roleId)), enabled, locked,
1149 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001150 if (!rc)
1151 {
1152 messages::resourceNotFound(
1153 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1154 username);
1155 return;
1156 }
1157
1158 if (password)
1159 {
1160 int retval = pamUpdatePassword(username, *password);
1161
1162 if (retval == PAM_USER_UNKNOWN)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001163 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001164 messages::resourceNotFound(
1165 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1166 username);
1167 }
1168 else if (retval == PAM_AUTHTOK_ERR)
1169 {
1170 // If password is invalid
1171 messages::propertyValueFormatError(asyncResp->res,
1172 *password, "Password");
1173 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1174 }
1175 else if (retval != PAM_SUCCESS)
1176 {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001177 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001178 return;
1179 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001180 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001181
Ed Tanous6c51eab2021-06-03 12:30:29 -07001182 if (enabled)
1183 {
1184 crow::connections::systemBus->async_method_call(
1185 [asyncResp](const boost::system::error_code ec) {
1186 if (ec)
1187 {
1188 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1189 messages::internalError(asyncResp->res);
1190 return;
1191 }
1192 messages::success(asyncResp->res);
1193 return;
1194 },
1195 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1196 "org.freedesktop.DBus.Properties", "Set",
1197 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1198 std::variant<bool>{*enabled});
1199 }
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001200
Ed Tanous6c51eab2021-06-03 12:30:29 -07001201 if (roleId)
1202 {
1203 std::string priv = getPrivilegeFromRoleId(*roleId);
1204 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001205 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001206 messages::propertyValueNotInList(asyncResp->res, *roleId,
1207 "RoleId");
1208 return;
1209 }
1210 if (priv == "priv-noaccess")
1211 {
1212 priv = "";
1213 }
1214
1215 crow::connections::systemBus->async_method_call(
1216 [asyncResp](const boost::system::error_code ec) {
1217 if (ec)
1218 {
1219 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1220 messages::internalError(asyncResp->res);
1221 return;
1222 }
1223 messages::success(asyncResp->res);
1224 },
1225 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1226 "org.freedesktop.DBus.Properties", "Set",
1227 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1228 std::variant<std::string>{priv});
1229 }
1230
1231 if (locked)
1232 {
1233 // admin can unlock the account which is locked by
1234 // successive authentication failures but admin should
1235 // not be allowed to lock an account.
1236 if (*locked)
1237 {
1238 messages::propertyValueNotInList(asyncResp->res, "true",
1239 "Locked");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001240 return;
1241 }
1242
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001243 crow::connections::systemBus->async_method_call(
Ed Tanous6c51eab2021-06-03 12:30:29 -07001244 [asyncResp](const boost::system::error_code ec) {
1245 if (ec)
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001246 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001247 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1248 messages::internalError(asyncResp->res);
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001249 return;
1250 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001251 messages::success(asyncResp->res);
1252 return;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001253 },
Ed Tanous6c51eab2021-06-03 12:30:29 -07001254 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1255 "org.freedesktop.DBus.Properties", "Set",
1256 "xyz.openbmc_project.User.Attributes",
1257 "UserLockedForFailedAttempt", std::variant<bool>{*locked});
1258 }
1259 });
1260}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001261
Ed Tanous6c51eab2021-06-03 12:30:29 -07001262inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001263{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001264
Ed Tanous6c51eab2021-06-03 12:30:29 -07001265 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001266 .privileges(redfish::privileges::getAccountService)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001267 .methods(
Abhishek Patel72048782021-06-02 09:53:24 -05001268 boost::beast::http::verb::get)([](const crow::Request& req,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001269 const std::shared_ptr<
1270 bmcweb::AsyncResp>& asyncResp)
1271 -> void {
1272 const persistent_data::AuthConfigMethods& authMethodsConfig =
1273 persistent_data::SessionStore::getInstance()
1274 .getAuthMethodsConfig();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001275
Ed Tanous6c51eab2021-06-03 12:30:29 -07001276 asyncResp->res.jsonValue = {
1277 {"@odata.id", "/redfish/v1/AccountService"},
1278 {"@odata.type", "#AccountService."
1279 "v1_5_0.AccountService"},
1280 {"Id", "AccountService"},
1281 {"Name", "Account Service"},
1282 {"Description", "Account Service"},
1283 {"ServiceEnabled", true},
1284 {"MaxPasswordLength", 20},
1285 {"Accounts",
1286 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
1287 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
1288 {"Oem",
1289 {{"OpenBMC",
1290 {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
Ed Tanousd8e3c292021-09-21 18:01:43 -07001291 {"@odata.id", "/redfish/v1/AccountService#/Oem/OpenBMC"},
Ed Tanous6c51eab2021-06-03 12:30:29 -07001292 {"AuthMethods",
1293 {
1294 {"BasicAuth", authMethodsConfig.basic},
1295 {"SessionToken", authMethodsConfig.sessionToken},
1296 {"XToken", authMethodsConfig.xtoken},
1297 {"Cookie", authMethodsConfig.cookie},
1298 {"TLS", authMethodsConfig.tls},
Abhishek Patel72048782021-06-02 09:53:24 -05001299 }}}}}}};
1300 // /redfish/v1/AccountService/LDAP/Certificates is something only
1301 // ConfigureManager can access then only display when the user has
1302 // permissions ConfigureManager
1303 Privileges effectiveUserPrivileges =
1304 redfish::getUserPrivileges(req.userRole);
1305
1306 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1307 effectiveUserPrivileges))
1308 {
1309 asyncResp->res.jsonValue["LDAP"] = {
1310 {"Certificates",
1311 {{"@odata.id",
1312 "/redfish/v1/AccountService/LDAP/Certificates"}}}};
1313 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001314 crow::connections::systemBus->async_method_call(
1315 [asyncResp](
1316 const boost::system::error_code ec,
1317 const std::vector<
1318 std::pair<std::string,
1319 std::variant<uint32_t, uint16_t, uint8_t>>>&
1320 propertiesList) {
1321 if (ec)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001322 {
1323 messages::internalError(asyncResp->res);
1324 return;
1325 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001326 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1327 << "properties for AccountService";
1328 for (const std::pair<std::string,
1329 std::variant<uint32_t, uint16_t,
1330 uint8_t>>& property :
1331 propertiesList)
1332 {
1333 if (property.first == "MinPasswordLength")
1334 {
1335 const uint8_t* value =
1336 std::get_if<uint8_t>(&property.second);
1337 if (value != nullptr)
Ratan Gupta24c85422019-01-30 19:41:24 +05301338 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001339 asyncResp->res.jsonValue["MinPasswordLength"] =
1340 *value;
Ratan Gupta24c85422019-01-30 19:41:24 +05301341 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001342 }
1343 if (property.first == "AccountUnlockTimeout")
1344 {
1345 const uint32_t* value =
1346 std::get_if<uint32_t>(&property.second);
1347 if (value != nullptr)
1348 {
1349 asyncResp->res
1350 .jsonValue["AccountLockoutDuration"] =
1351 *value;
1352 }
1353 }
1354 if (property.first == "MaxLoginAttemptBeforeLockout")
1355 {
1356 const uint16_t* value =
1357 std::get_if<uint16_t>(&property.second);
1358 if (value != nullptr)
1359 {
1360 asyncResp->res
1361 .jsonValue["AccountLockoutThreshold"] =
1362 *value;
1363 }
1364 }
1365 }
1366 },
1367 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1368 "org.freedesktop.DBus.Properties", "GetAll",
1369 "xyz.openbmc_project.User.AccountPolicy");
1370
1371 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
1372 const std::string& ldapType) {
1373 if (!success)
1374 {
1375 return;
1376 }
1377 parseLDAPConfigData(asyncResp->res.jsonValue, confData,
1378 ldapType);
1379 };
1380
1381 getLDAPConfigData("LDAP", callback);
1382 getLDAPConfigData("ActiveDirectory", callback);
1383 });
1384
Ed Tanousf5ffd802021-07-19 10:55:33 -07001385 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
1386 .privileges(redfish::privileges::getAccountService)
1387 .methods(boost::beast::http::verb::patch)(
1388 [](const crow::Request& req,
1389 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1390 std::optional<uint32_t> unlockTimeout;
1391 std::optional<uint16_t> lockoutThreshold;
1392 std::optional<uint16_t> minPasswordLength;
1393 std::optional<uint16_t> maxPasswordLength;
1394 std::optional<nlohmann::json> ldapObject;
1395 std::optional<nlohmann::json> activeDirectoryObject;
1396 std::optional<nlohmann::json> oemObject;
1397
1398 if (!json_util::readJson(
1399 req, asyncResp->res, "AccountLockoutDuration",
1400 unlockTimeout, "AccountLockoutThreshold",
1401 lockoutThreshold, "MaxPasswordLength",
1402 maxPasswordLength, "MinPasswordLength",
1403 minPasswordLength, "LDAP", ldapObject,
1404 "ActiveDirectory", activeDirectoryObject, "Oem",
1405 oemObject))
1406 {
1407 return;
1408 }
1409
1410 if (minPasswordLength)
1411 {
1412 messages::propertyNotWritable(asyncResp->res,
1413 "MinPasswordLength");
1414 }
1415
1416 if (maxPasswordLength)
1417 {
1418 messages::propertyNotWritable(asyncResp->res,
1419 "MaxPasswordLength");
1420 }
1421
1422 if (ldapObject)
1423 {
1424 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1425 }
1426
1427 if (std::optional<nlohmann::json> oemOpenBMCObject;
1428 oemObject &&
1429 json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1430 oemOpenBMCObject))
1431 {
1432 if (std::optional<nlohmann::json> authMethodsObject;
1433 oemOpenBMCObject &&
1434 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1435 "AuthMethods", authMethodsObject))
1436 {
1437 if (authMethodsObject)
1438 {
1439 handleAuthMethodsPatch(*authMethodsObject,
1440 asyncResp);
1441 }
1442 }
1443 }
1444
1445 if (activeDirectoryObject)
1446 {
1447 handleLDAPPatch(*activeDirectoryObject, asyncResp,
1448 "ActiveDirectory");
1449 }
1450
1451 if (unlockTimeout)
1452 {
1453 crow::connections::systemBus->async_method_call(
1454 [asyncResp](const boost::system::error_code ec) {
1455 if (ec)
1456 {
1457 messages::internalError(asyncResp->res);
1458 return;
1459 }
1460 messages::success(asyncResp->res);
1461 },
1462 "xyz.openbmc_project.User.Manager",
1463 "/xyz/openbmc_project/user",
1464 "org.freedesktop.DBus.Properties", "Set",
1465 "xyz.openbmc_project.User.AccountPolicy",
1466 "AccountUnlockTimeout",
1467 std::variant<uint32_t>(*unlockTimeout));
1468 }
1469 if (lockoutThreshold)
1470 {
1471 crow::connections::systemBus->async_method_call(
1472 [asyncResp](const boost::system::error_code ec) {
1473 if (ec)
1474 {
1475 messages::internalError(asyncResp->res);
1476 return;
1477 }
1478 messages::success(asyncResp->res);
1479 },
1480 "xyz.openbmc_project.User.Manager",
1481 "/xyz/openbmc_project/user",
1482 "org.freedesktop.DBus.Properties", "Set",
1483 "xyz.openbmc_project.User.AccountPolicy",
1484 "MaxLoginAttemptBeforeLockout",
1485 std::variant<uint16_t>(*lockoutThreshold));
1486 }
1487 });
1488
Ed Tanous6c51eab2021-06-03 12:30:29 -07001489 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001490 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001491 .methods(boost::beast::http::verb::get)(
1492 [](const crow::Request& req,
1493 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1494 asyncResp->res.jsonValue = {
1495 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
1496 {"@odata.type", "#ManagerAccountCollection."
1497 "ManagerAccountCollection"},
1498 {"Name", "Accounts Collection"},
1499 {"Description", "BMC User Accounts"}};
1500
Ed Tanous6c51eab2021-06-03 12:30:29 -07001501 Privileges effectiveUserPrivileges =
1502 redfish::getUserPrivileges(req.userRole);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001503
JunLin Chenf5e29f32021-12-08 16:47:04 +08001504 std::string thisUser;
1505 if (req.session)
1506 {
1507 thisUser = req.session->username;
1508 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001509 crow::connections::systemBus->async_method_call(
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001510 [asyncResp, thisUser, effectiveUserPrivileges](
1511 const boost::system::error_code ec,
1512 const ManagedObjectType& users) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001513 if (ec)
1514 {
1515 messages::internalError(asyncResp->res);
Ratan Gupta24c85422019-01-30 19:41:24 +05301516 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001517 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001518
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001519 bool userCanSeeAllAccounts =
1520 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001521 {"ConfigureUsers"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001522
1523 bool userCanSeeSelf =
1524 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001525 {"ConfigureSelf"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001526
Ed Tanous6c51eab2021-06-03 12:30:29 -07001527 nlohmann::json& memberArray =
1528 asyncResp->res.jsonValue["Members"];
1529 memberArray = nlohmann::json::array();
Ratan Gupta24c85422019-01-30 19:41:24 +05301530
Ed Tanous6c51eab2021-06-03 12:30:29 -07001531 for (auto& userpath : users)
1532 {
1533 std::string user = userpath.first.filename();
1534 if (user.empty())
Ratan Gupta24c85422019-01-30 19:41:24 +05301535 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301536 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001537 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1538
Ratan Gupta24c85422019-01-30 19:41:24 +05301539 return;
1540 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301541
Ed Tanous6c51eab2021-06-03 12:30:29 -07001542 // As clarified by Redfish here:
1543 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1544 // Users without ConfigureUsers, only see their own
1545 // account. Users with ConfigureUsers, see all
1546 // accounts.
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001547 if (userCanSeeAllAccounts ||
1548 (thisUser == user && userCanSeeSelf))
Ratan Gupta24c85422019-01-30 19:41:24 +05301549 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001550 memberArray.push_back(
1551 {{"@odata.id",
1552 "/redfish/v1/AccountService/Accounts/" +
1553 user}});
Ratan Gupta24c85422019-01-30 19:41:24 +05301554 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001555 }
1556 asyncResp->res.jsonValue["Members@odata.count"] =
1557 memberArray.size();
1558 },
1559 "xyz.openbmc_project.User.Manager",
1560 "/xyz/openbmc_project/user",
1561 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Ratan Gupta24c85422019-01-30 19:41:24 +05301562 });
Ed Tanous06e086d2018-09-19 17:19:52 -07001563
Ed Tanous6c51eab2021-06-03 12:30:29 -07001564 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001565 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001566 .methods(boost::beast::http::verb::post)([](const crow::Request& req,
1567 const std::shared_ptr<
1568 bmcweb::AsyncResp>&
1569 asyncResp) -> void {
1570 std::string username;
1571 std::string password;
1572 std::optional<std::string> roleId("User");
1573 std::optional<bool> enabled = true;
1574 if (!json_util::readJson(req, asyncResp->res, "UserName", username,
1575 "Password", password, "RoleId", roleId,
1576 "Enabled", enabled))
1577 {
1578 return;
1579 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001580
Ed Tanous6c51eab2021-06-03 12:30:29 -07001581 std::string priv = getPrivilegeFromRoleId(*roleId);
1582 if (priv.empty())
1583 {
1584 messages::propertyValueNotInList(asyncResp->res, *roleId,
1585 "RoleId");
1586 return;
1587 }
1588 // TODO: Following override will be reverted once support in
1589 // phosphor-user-manager is added. In order to avoid dependency
1590 // issues, this is added in bmcweb, which will removed, once
1591 // phosphor-user-manager supports priv-noaccess.
1592 if (priv == "priv-noaccess")
1593 {
1594 roleId = "";
1595 }
1596 else
1597 {
1598 roleId = priv;
1599 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001600
Ed Tanous6c51eab2021-06-03 12:30:29 -07001601 // Reading AllGroups property
1602 crow::connections::systemBus->async_method_call(
1603 [asyncResp, username, password{std::move(password)}, roleId,
1604 enabled](
1605 const boost::system::error_code ec,
1606 const std::variant<std::vector<std::string>>& allGroups) {
1607 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
1641 crow::connections::systemBus->async_method_call(
1642 [asyncResp, password](
1643 const boost::system::error_code ec3) {
1644 if (ec3)
1645 {
1646 messages::internalError(
1647 asyncResp->res);
1648 return;
1649 }
1650
1651 // If password is invalid
1652 messages::propertyValueFormatError(
1653 asyncResp->res, password,
1654 "Password");
1655 },
1656 "xyz.openbmc_project.User.Manager",
1657 "/xyz/openbmc_project/user/" + username,
1658 "xyz.openbmc_project.Object.Delete",
1659 "Delete");
1660
1661 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1662 return;
1663 }
1664
1665 messages::created(asyncResp->res);
1666 asyncResp->res.addHeader(
1667 "Location",
1668 "/redfish/v1/AccountService/Accounts/" +
1669 username);
1670 },
1671 "xyz.openbmc_project.User.Manager",
1672 "/xyz/openbmc_project/user",
1673 "xyz.openbmc_project.User.Manager", "CreateUser",
1674 username, *allGroupsList, *roleId, *enabled);
1675 },
1676 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1677 "org.freedesktop.DBus.Properties", "Get",
1678 "xyz.openbmc_project.User.Manager", "AllGroups");
1679 });
1680
1681 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001682 .privileges(redfish::privileges::getManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001683 .methods(
1684 boost::beast::http::verb::
1685 get)([](const crow::Request& req,
1686 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1687 const std::string& accountName) -> void {
1688 if (req.session->username != accountName)
1689 {
1690 // At this point we've determined that the user is trying to
1691 // modify a user that isn't them. We need to verify that they
1692 // have permissions to modify other users, so re-run the auth
1693 // check with the same permissions, minus ConfigureSelf.
1694 Privileges effectiveUserPrivileges =
1695 redfish::getUserPrivileges(req.userRole);
1696 Privileges requiredPermissionsToChangeNonSelf = {
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001697 "ConfigureUsers", "ConfigureManager"};
Ed Tanous6c51eab2021-06-03 12:30:29 -07001698 if (!effectiveUserPrivileges.isSupersetOf(
1699 requiredPermissionsToChangeNonSelf))
Ed Tanous06e086d2018-09-19 17:19:52 -07001700 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001701 BMCWEB_LOG_DEBUG << "GET Account denied access";
1702 messages::insufficientPrivilege(asyncResp->res);
1703 return;
1704 }
1705 }
1706
1707 crow::connections::systemBus->async_method_call(
1708 [asyncResp, accountName](const boost::system::error_code ec,
1709 const ManagedObjectType& users) {
1710 if (ec)
1711 {
1712 messages::internalError(asyncResp->res);
1713 return;
1714 }
1715 auto userIt = users.begin();
1716
1717 for (; userIt != users.end(); userIt++)
1718 {
1719 if (boost::ends_with(userIt->first.str,
1720 "/" + accountName))
1721 {
1722 break;
1723 }
1724 }
1725 if (userIt == users.end())
1726 {
1727 messages::resourceNotFound(
1728 asyncResp->res, "ManagerAccount", accountName);
1729 return;
1730 }
1731
1732 asyncResp->res.jsonValue = {
1733 {"@odata.type",
1734 "#ManagerAccount.v1_4_0.ManagerAccount"},
1735 {"Name", "User Account"},
1736 {"Description", "User Account"},
1737 {"Password", nullptr},
1738 {"AccountTypes", {"Redfish"}}};
1739
1740 for (const auto& interface : userIt->second)
1741 {
1742 if (interface.first ==
1743 "xyz.openbmc_project.User.Attributes")
1744 {
1745 for (const auto& property : interface.second)
1746 {
1747 if (property.first == "UserEnabled")
1748 {
1749 const bool* userEnabled =
1750 std::get_if<bool>(&property.second);
1751 if (userEnabled == nullptr)
1752 {
1753 BMCWEB_LOG_ERROR
1754 << "UserEnabled wasn't a bool";
1755 messages::internalError(asyncResp->res);
1756 return;
1757 }
1758 asyncResp->res.jsonValue["Enabled"] =
1759 *userEnabled;
1760 }
1761 else if (property.first ==
1762 "UserLockedForFailedAttempt")
1763 {
1764 const bool* userLocked =
1765 std::get_if<bool>(&property.second);
1766 if (userLocked == nullptr)
1767 {
1768 BMCWEB_LOG_ERROR << "UserLockedForF"
1769 "ailedAttempt "
1770 "wasn't a bool";
1771 messages::internalError(asyncResp->res);
1772 return;
1773 }
1774 asyncResp->res.jsonValue["Locked"] =
1775 *userLocked;
1776 asyncResp->res.jsonValue
1777 ["Locked@Redfish.AllowableValues"] = {
1778 "false"}; // can only unlock accounts
1779 }
1780 else if (property.first == "UserPrivilege")
1781 {
1782 const std::string* userPrivPtr =
1783 std::get_if<std::string>(
1784 &property.second);
1785 if (userPrivPtr == nullptr)
1786 {
1787 BMCWEB_LOG_ERROR
1788 << "UserPrivilege wasn't a "
1789 "string";
1790 messages::internalError(asyncResp->res);
1791 return;
1792 }
1793 std::string role =
1794 getRoleIdFromPrivilege(*userPrivPtr);
1795 if (role.empty())
1796 {
1797 BMCWEB_LOG_ERROR << "Invalid user role";
1798 messages::internalError(asyncResp->res);
1799 return;
1800 }
1801 asyncResp->res.jsonValue["RoleId"] = role;
1802
1803 asyncResp->res.jsonValue["Links"]["Role"] =
1804 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +08001805 "/redfish/v1/AccountService/Roles/" +
Ed Tanous6c51eab2021-06-03 12:30:29 -07001806 role}};
1807 }
1808 else if (property.first ==
1809 "UserPasswordExpired")
1810 {
1811 const bool* userPasswordExpired =
1812 std::get_if<bool>(&property.second);
1813 if (userPasswordExpired == nullptr)
1814 {
George Liu0fda0f12021-11-16 10:06:17 +08001815 BMCWEB_LOG_ERROR
1816 << "UserPasswordExpired wasn't a bool";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001817 messages::internalError(asyncResp->res);
1818 return;
1819 }
1820 asyncResp->res
1821 .jsonValue["PasswordChangeRequired"] =
1822 *userPasswordExpired;
1823 }
1824 }
1825 }
1826 }
1827
1828 asyncResp->res.jsonValue["@odata.id"] =
1829 "/redfish/v1/AccountService/Accounts/" + accountName;
1830 asyncResp->res.jsonValue["Id"] = accountName;
1831 asyncResp->res.jsonValue["UserName"] = accountName;
1832 },
1833 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1834 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1835 });
1836
1837 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001838 // TODO this privilege should be using the generated endpoints, but
1839 // because of the special handling of ConfigureSelf, it's not able to
1840 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001841 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1842 .methods(boost::beast::http::verb::patch)(
1843 [](const crow::Request& req,
1844 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1845 const std::string& username) -> void {
1846 std::optional<std::string> newUserName;
1847 std::optional<std::string> password;
1848 std::optional<bool> enabled;
1849 std::optional<std::string> roleId;
1850 std::optional<bool> locked;
Ed Tanouse9cc5172021-11-03 14:13:19 +08001851
1852 Privileges effectiveUserPrivileges =
1853 redfish::getUserPrivileges(req.userRole);
1854 Privileges configureUsers = {"ConfigureUsers"};
1855 bool userHasConfigureUsers =
1856 effectiveUserPrivileges.isSupersetOf(configureUsers);
1857 if (userHasConfigureUsers)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001858 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001859 // Users with ConfigureUsers can modify for all users
1860 if (!json_util::readJson(req, asyncResp->res, "UserName",
1861 newUserName, "Password", password,
1862 "RoleId", roleId, "Enabled",
1863 enabled, "Locked", locked))
1864 {
1865 return;
1866 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001867 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001868 else
Ed Tanous6c51eab2021-06-03 12:30:29 -07001869 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001870 // ConfigureSelf accounts can only modify their own account
1871 if (username != req.session->username)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001872 {
1873 messages::insufficientPrivilege(asyncResp->res);
1874 return;
1875 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001876 // ConfigureSelf accounts can only modify their password
1877 if (!json_util::readJson(req, asyncResp->res, "Password",
1878 password))
1879 {
1880 return;
1881 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001882 }
1883
1884 // if user name is not provided in the patch method or if it
1885 // matches the user name in the URI, then we are treating it as
1886 // updating user properties other then username. If username
1887 // provided doesn't match the URI, then we are treating this as
1888 // user rename request.
1889 if (!newUserName || (newUserName.value() == username))
1890 {
1891 updateUserProperties(asyncResp, username, password, enabled,
1892 roleId, locked);
1893 return;
1894 }
1895 crow::connections::systemBus->async_method_call(
1896 [asyncResp, username, password(std::move(password)),
1897 roleId(std::move(roleId)), enabled,
1898 newUser{std::string(*newUserName)},
1899 locked](const boost::system::error_code ec,
1900 sdbusplus::message::message& m) {
1901 if (ec)
1902 {
1903 userErrorMessageHandler(m.get_error(), asyncResp,
1904 newUser, username);
1905 return;
1906 }
1907
1908 updateUserProperties(asyncResp, newUser, password,
1909 enabled, roleId, locked);
1910 },
1911 "xyz.openbmc_project.User.Manager",
1912 "/xyz/openbmc_project/user",
1913 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1914 *newUserName);
1915 });
1916
1917 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001918 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001919 .methods(boost::beast::http::verb::delete_)(
1920 [](const crow::Request& /*req*/,
1921 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1922 const std::string& username) -> void {
1923 const std::string userPath =
1924 "/xyz/openbmc_project/user/" + username;
1925
1926 crow::connections::systemBus->async_method_call(
1927 [asyncResp, username](const boost::system::error_code ec) {
1928 if (ec)
1929 {
1930 messages::resourceNotFound(
1931 asyncResp->res,
1932 "#ManagerAccount.v1_4_0.ManagerAccount",
1933 username);
1934 return;
1935 }
1936
1937 messages::accountRemoved(asyncResp->res);
1938 },
1939 "xyz.openbmc_project.User.Manager", userPath,
1940 "xyz.openbmc_project.Object.Delete", "Delete");
1941 });
1942}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001943
Ed Tanous1abe55e2018-09-05 08:30:59 -07001944} // namespace redfish