blob: 291a207d029319ddee32532e12c00f1aa42f10ac [file] [log] [blame]
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010017#include "node.hpp"
18
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 Tanousa8408792018-09-05 16:08:38 -070022#include <utils/json_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080023#include <variant>
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070024
Ed Tanous1abe55e2018-09-05 08:30:59 -070025namespace redfish
26{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010027
Ratan Gupta6973a582018-12-13 18:25:44 +053028constexpr const char* ldapConfigObject =
29 "/xyz/openbmc_project/user/ldap/openldap";
Ratan Guptaab828d72019-04-22 14:18:33 +053030constexpr const char* ADConfigObject =
31 "/xyz/openbmc_project/user/ldap/active_directory";
32
Ratan Gupta6973a582018-12-13 18:25:44 +053033constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
34constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
35constexpr const char* ldapConfigInterface =
36 "xyz.openbmc_project.User.Ldap.Config";
37constexpr const char* ldapCreateInterface =
38 "xyz.openbmc_project.User.Ldap.Create";
39constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053040constexpr const char* ldapPrivMapperInterface =
41 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053042constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
43constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
44constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
45constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
46constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
47
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060048struct LDAPRoleMapData
49{
50 std::string groupName;
51 std::string privilege;
52};
53
Ratan Gupta6973a582018-12-13 18:25:44 +053054struct LDAPConfigData
55{
56 std::string uri{};
57 std::string bindDN{};
58 std::string baseDN{};
59 std::string searchScope{};
60 std::string serverType{};
61 bool serviceEnabled = false;
62 std::string userNameAttribute{};
63 std::string groupAttribute{};
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060064 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053065};
66
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070067using ManagedObjectType = std::vector<std::pair<
68 sdbusplus::message::object_path,
69 boost::container::flat_map<
Ed Tanousabf2add2019-01-22 16:40:12 -080070 std::string, boost::container::flat_map<
71 std::string, std::variant<bool, std::string>>>>>;
Ratan Gupta6973a582018-12-13 18:25:44 +053072using GetObjectType =
73 std::vector<std::pair<std::string, std::vector<std::string>>>;
AppaRao Puli84e12cb2018-10-11 01:28:15 +053074
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060075inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053076{
77 if (role == "priv-admin")
78 {
79 return "Administrator";
80 }
81 else if (role == "priv-callback")
82 {
83 return "Callback";
84 }
85 else if (role == "priv-user")
86 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053087 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053088 }
89 else if (role == "priv-operator")
90 {
91 return "Operator";
92 }
jayaprakash Mutyala27c10d22019-07-29 11:59:08 +000093 else if ((role == "") || (role == "priv-noaccess"))
94 {
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 }
105 else if (role == "Callback")
106 {
107 return "priv-callback";
108 }
AppaRao Pulic80fee52019-10-16 14:49:36 +0530109 else if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530110 {
111 return "priv-user";
112 }
113 else if (role == "Operator")
114 {
115 return "priv-operator";
116 }
jayaprakash Mutyala27c10d22019-07-29 11:59:08 +0000117 else if (role == "NoAccess")
118 {
119 return "priv-noaccess";
120 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530121 return "";
122}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700123
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000124void userErrorMessageHandler(const sd_bus_error* e,
125 std::shared_ptr<AsyncResp> asyncResp,
126 const std::string& newUser,
127 const std::string& username)
128{
129 const char* errorMessage = e->name;
130 if (e == nullptr)
131 {
132 messages::internalError(asyncResp->res);
133 return;
134 }
135
136 if (strcmp(errorMessage,
137 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
138 {
139 messages::resourceAlreadyExists(asyncResp->res,
140 "#ManagerAccount.v1_0_3.ManagerAccount",
141 "UserName", newUser);
142 }
143 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
144 "UserNameDoesNotExist") == 0)
145 {
146 messages::resourceNotFound(
147 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount", username);
148 }
149 else if (strcmp(errorMessage,
150 "xyz.openbmc_project.Common.Error.InvalidArgument") == 0)
151 {
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 }
159 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
160 "UserNameGroupFail") == 0)
161 {
162 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
163 }
164 else
165 {
166 messages::internalError(asyncResp->res);
167 }
168
169 return;
170}
171
Ratan Gupta6973a582018-12-13 18:25:44 +0530172void parseLDAPConfigData(nlohmann::json& json_response,
Ratan Guptaab828d72019-04-22 14:18:33 +0530173 const LDAPConfigData& confData,
174 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530175{
Ratan Guptaab828d72019-04-22 14:18:33 +0530176 std::string service =
177 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600178 nlohmann::json ldap = {
Ratan Gupta6973a582018-12-13 18:25:44 +0530179 {"AccountProviderType", service},
Ratan Gupta6973a582018-12-13 18:25:44 +0530180 {"ServiceEnabled", confData.serviceEnabled},
181 {"ServiceAddresses", nlohmann::json::array({confData.uri})},
182 {"Authentication",
183 {{"AuthenticationType", "UsernameAndPassword"},
Ratan Gupta6973a582018-12-13 18:25:44 +0530184 {"Username", confData.bindDN},
185 {"Password", nullptr}}},
186 {"LDAPService",
187 {{"SearchSettings",
188 {{"BaseDistinguishedNames",
189 nlohmann::json::array({confData.baseDN})},
190 {"UsernameAttribute", confData.userNameAttribute},
191 {"GroupsAttribute", confData.groupAttribute}}}}},
192 };
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600193
Marri Devender Rao37cce912019-02-20 01:05:22 -0600194 json_response[ldapType].update(std::move(ldap));
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600195
196 nlohmann::json& roleMapArray = json_response[ldapType]["RemoteRoleMapping"];
197 roleMapArray = nlohmann::json::array();
198 for (auto& obj : confData.groupRoleList)
199 {
200 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
201 << obj.second.groupName << "\n";
202 roleMapArray.push_back(
203 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
204 nlohmann::json::array(
205 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
206 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530207}
208
209/**
Ratan Gupta06785242019-07-26 22:30:16 +0530210 * @brief validates given JSON input and then calls appropriate method to
211 * create, to delete or to set Rolemapping object based on the given input.
212 *
213 */
214static void handleRoleMapPatch(
215 const std::shared_ptr<AsyncResp>& asyncResp,
216 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
217 const std::string& serverType, std::vector<nlohmann::json>& input)
218{
219 for (size_t index = 0; index < input.size(); index++)
220 {
221 nlohmann::json& thisJson = input[index];
222
223 if (thisJson.is_null())
224 {
225 // delete the existing object
226 if (index < roleMapObjData.size())
227 {
228 crow::connections::systemBus->async_method_call(
229 [asyncResp, roleMapObjData, serverType,
230 index](const boost::system::error_code ec) {
231 if (ec)
232 {
233 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
234 messages::internalError(asyncResp->res);
235 return;
236 }
237 asyncResp->res
238 .jsonValue[serverType]["RemoteRoleMapping"][index] =
239 nullptr;
240 },
241 ldapDbusService, roleMapObjData[index].first,
242 "xyz.openbmc_project.Object.Delete", "Delete");
243 }
244 else
245 {
246 BMCWEB_LOG_ERROR << "Can't delete the object";
247 messages::propertyValueTypeError(
248 asyncResp->res, thisJson.dump(),
249 "RemoteRoleMapping/" + std::to_string(index));
250 return;
251 }
252 }
253 else if (thisJson.empty())
254 {
255 // Don't do anything for the empty objects,parse next json
256 // eg {"RemoteRoleMapping",[{}]}
257 }
258 else
259 {
260 // update/create the object
261 std::optional<std::string> remoteGroup;
262 std::optional<std::string> localRole;
263
264 if (!json_util::readJson(thisJson, asyncResp->res, "RemoteGroup",
265 remoteGroup, "LocalRole", localRole))
266 {
267 continue;
268 }
269
270 // Update existing RoleMapping Object
271 if (index < roleMapObjData.size())
272 {
273 BMCWEB_LOG_DEBUG << "Update Role Map Object";
274 // If "RemoteGroup" info is provided
275 if (remoteGroup)
276 {
277 crow::connections::systemBus->async_method_call(
278 [asyncResp, roleMapObjData, serverType, index,
279 remoteGroup](const boost::system::error_code ec) {
280 if (ec)
281 {
282 BMCWEB_LOG_ERROR << "DBUS response error: "
283 << ec;
284 messages::internalError(asyncResp->res);
285 return;
286 }
287 asyncResp->res
288 .jsonValue[serverType]["RemoteRoleMapping"]
289 [index]["RemoteGroup"] = *remoteGroup;
290 },
291 ldapDbusService, roleMapObjData[index].first,
292 propertyInterface, "Set",
293 "xyz.openbmc_project.User.PrivilegeMapperEntry",
294 "GroupName",
295 std::variant<std::string>(std::move(*remoteGroup)));
296 }
297
298 // If "LocalRole" info is provided
299 if (localRole)
300 {
301 crow::connections::systemBus->async_method_call(
302 [asyncResp, roleMapObjData, serverType, index,
303 localRole](const boost::system::error_code ec) {
304 if (ec)
305 {
306 BMCWEB_LOG_ERROR << "DBUS response error: "
307 << ec;
308 messages::internalError(asyncResp->res);
309 return;
310 }
311 asyncResp->res
312 .jsonValue[serverType]["RemoteRoleMapping"]
313 [index]["LocalRole"] = *localRole;
314 },
315 ldapDbusService, roleMapObjData[index].first,
316 propertyInterface, "Set",
317 "xyz.openbmc_project.User.PrivilegeMapperEntry",
318 "Privilege",
319 std::variant<std::string>(
320 getPrivilegeFromRoleId(std::move(*localRole))));
321 }
322 }
323 // Create a new RoleMapping Object.
324 else
325 {
326 BMCWEB_LOG_DEBUG
327 << "setRoleMappingProperties: Creating new Object";
328 std::string pathString =
329 "RemoteRoleMapping/" + std::to_string(index);
330
331 if (!localRole)
332 {
333 messages::propertyMissing(asyncResp->res,
334 pathString + "/LocalRole");
335 continue;
336 }
337 if (!remoteGroup)
338 {
339 messages::propertyMissing(asyncResp->res,
340 pathString + "/RemoteGroup");
341 continue;
342 }
343
344 std::string dbusObjectPath;
345 if (serverType == "ActiveDirectory")
346 {
347 dbusObjectPath = ADConfigObject;
348 }
349 else if (serverType == "LDAP")
350 {
351 dbusObjectPath = ldapConfigObject;
352 }
353
354 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
355 << ",LocalRole=" << *localRole;
356
357 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700358 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530359 remoteGroup](const boost::system::error_code ec) {
360 if (ec)
361 {
362 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
363 messages::internalError(asyncResp->res);
364 return;
365 }
366 nlohmann::json& remoteRoleJson =
367 asyncResp->res
368 .jsonValue[serverType]["RemoteRoleMapping"];
369 remoteRoleJson.push_back(
370 {{"LocalRole", *localRole},
371 {"RemoteGroup", *remoteGroup}});
372 },
373 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
374 "Create", std::move(*remoteGroup),
375 getPrivilegeFromRoleId(std::move(*localRole)));
376 }
377 }
378 }
379}
380
381/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530382 * Function that retrieves all properties for LDAP config object
383 * into JSON
384 */
385template <typename CallbackFunc>
386inline void getLDAPConfigData(const std::string& ldapType,
387 CallbackFunc&& callback)
388{
Ratan Guptaab828d72019-04-22 14:18:33 +0530389
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600390 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530391 ldapConfigInterface};
392
393 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600394 [callback, ldapType](const boost::system::error_code ec,
395 const GetObjectType& resp) {
396 LDAPConfigData confData{};
397 if (ec || resp.empty())
398 {
399 BMCWEB_LOG_ERROR << "DBUS response error during getting of "
400 "service name: "
401 << ec;
402 callback(false, confData, ldapType);
403 return;
404 }
405 std::string service = resp.begin()->first;
406 crow::connections::systemBus->async_method_call(
407 [callback, ldapType](const boost::system::error_code error_code,
408 const ManagedObjectType& ldapObjects) {
409 LDAPConfigData confData{};
410 if (error_code)
411 {
412 callback(false, confData, ldapType);
413 BMCWEB_LOG_ERROR << "D-Bus responses error: "
414 << error_code;
415 return;
416 }
417
418 std::string ldapDbusType;
419 std::string searchString;
420
421 if (ldapType == "LDAP")
422 {
423 ldapDbusType = "xyz.openbmc_project.User.Ldap.Config."
424 "Type.OpenLdap";
425 searchString = "openldap";
426 }
427 else if (ldapType == "ActiveDirectory")
428 {
429 ldapDbusType =
430 "xyz.openbmc_project.User.Ldap.Config.Type."
431 "ActiveDirectory";
432 searchString = "active_directory";
433 }
434 else
435 {
436 BMCWEB_LOG_ERROR
437 << "Can't get the DbusType for the given type="
438 << ldapType;
439 callback(false, confData, ldapType);
440 return;
441 }
442
443 std::string ldapEnableInterfaceStr = ldapEnableInterface;
444 std::string ldapConfigInterfaceStr = ldapConfigInterface;
445
446 for (const auto& object : ldapObjects)
447 {
448 // let's find the object whose ldap type is equal to the
449 // given type
450 if (object.first.str.find(searchString) ==
451 std::string::npos)
452 {
453 continue;
454 }
455
456 for (const auto& interface : object.second)
457 {
458 if (interface.first == ldapEnableInterfaceStr)
459 {
460 // rest of the properties are string.
461 for (const auto& property : interface.second)
462 {
463 if (property.first == "Enabled")
464 {
465 const bool* value =
466 std::get_if<bool>(&property.second);
467 if (value == nullptr)
468 {
469 continue;
470 }
471 confData.serviceEnabled = *value;
472 break;
473 }
474 }
475 }
476 else if (interface.first == ldapConfigInterfaceStr)
477 {
478
479 for (const auto& property : interface.second)
480 {
Ed Tanous271584a2019-07-09 16:24:22 -0700481 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600482 std::get_if<std::string>(
483 &property.second);
Ed Tanous271584a2019-07-09 16:24:22 -0700484 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600485 {
486 continue;
487 }
488 if (property.first == "LDAPServerURI")
489 {
Ed Tanous271584a2019-07-09 16:24:22 -0700490 confData.uri = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600491 }
492 else if (property.first == "LDAPBindDN")
493 {
Ed Tanous271584a2019-07-09 16:24:22 -0700494 confData.bindDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600495 }
496 else if (property.first == "LDAPBaseDN")
497 {
Ed Tanous271584a2019-07-09 16:24:22 -0700498 confData.baseDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600499 }
500 else if (property.first ==
501 "LDAPSearchScope")
502 {
Ed Tanous271584a2019-07-09 16:24:22 -0700503 confData.searchScope = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600504 }
505 else if (property.first ==
506 "GroupNameAttribute")
507 {
Ed Tanous271584a2019-07-09 16:24:22 -0700508 confData.groupAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600509 }
510 else if (property.first ==
511 "UserNameAttribute")
512 {
Ed Tanous271584a2019-07-09 16:24:22 -0700513 confData.userNameAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600514 }
515 else if (property.first == "LDAPType")
516 {
Ed Tanous271584a2019-07-09 16:24:22 -0700517 confData.serverType = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600518 }
519 }
520 }
521 else if (interface.first ==
522 "xyz.openbmc_project.User."
523 "PrivilegeMapperEntry")
524 {
525 LDAPRoleMapData roleMapData{};
526 for (const auto& property : interface.second)
527 {
Ed Tanous271584a2019-07-09 16:24:22 -0700528 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600529 std::get_if<std::string>(
530 &property.second);
531
Ed Tanous271584a2019-07-09 16:24:22 -0700532 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600533 {
534 continue;
535 }
536
537 if (property.first == "GroupName")
538 {
Ed Tanous271584a2019-07-09 16:24:22 -0700539 roleMapData.groupName = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600540 }
541 else if (property.first == "Privilege")
542 {
Ed Tanous271584a2019-07-09 16:24:22 -0700543 roleMapData.privilege = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600544 }
545 }
546
Ed Tanous0f0353b2019-10-24 11:37:51 -0700547 confData.groupRoleList.emplace_back(
548 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600549 }
550 }
551 }
552 callback(true, confData, ldapType);
553 },
554 service, ldapRootObject, dbusObjManagerIntf,
555 "GetManagedObjects");
556 },
557 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
558 ldapConfigObject, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530559}
560
Ed Tanous1abe55e2018-09-05 08:30:59 -0700561class AccountService : public Node
562{
563 public:
Zbigniew Kurzynski78158632019-11-05 12:57:37 +0100564 AccountService(CrowApp& app) :
565 Node(app, "/redfish/v1/AccountService/"), app(app)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700566 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700567 entityPrivileges = {
568 {boost::beast::http::verb::get,
569 {{"ConfigureUsers"}, {"ConfigureManager"}}},
570 {boost::beast::http::verb::head, {{"Login"}}},
571 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
572 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
573 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
574 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
575 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +0100576
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 private:
Ratan Gupta8a07d282019-03-16 08:33:47 +0530578 /**
579 * @brief parses the authentication section under the LDAP
580 * @param input JSON data
581 * @param asyncResp pointer to the JSON response
582 * @param userName userName to be filled from the given JSON.
583 * @param password password to be filled from the given JSON.
584 */
585 void
586 parseLDAPAuthenticationJson(nlohmann::json input,
587 const std::shared_ptr<AsyncResp>& asyncResp,
588 std::optional<std::string>& username,
589 std::optional<std::string>& password)
590 {
591 std::optional<std::string> authType;
592
593 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
594 authType, "Username", username, "Password",
595 password))
596 {
597 return;
598 }
599 if (!authType)
600 {
601 return;
602 }
603 if (*authType != "UsernameAndPassword")
604 {
605 messages::propertyValueNotInList(asyncResp->res, *authType,
606 "AuthenticationType");
607 return;
608 }
609 }
610 /**
611 * @brief parses the LDAPService section under the LDAP
612 * @param input JSON data
613 * @param asyncResp pointer to the JSON response
614 * @param baseDNList baseDN to be filled from the given JSON.
615 * @param userNameAttribute userName to be filled from the given JSON.
616 * @param groupaAttribute password to be filled from the given JSON.
617 */
618
619 void parseLDAPServiceJson(
620 nlohmann::json input, const std::shared_ptr<AsyncResp>& asyncResp,
621 std::optional<std::vector<std::string>>& baseDNList,
622 std::optional<std::string>& userNameAttribute,
623 std::optional<std::string>& groupsAttribute)
624 {
625 std::optional<nlohmann::json> searchSettings;
626
627 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
628 searchSettings))
629 {
630 return;
631 }
632 if (!searchSettings)
633 {
634 return;
635 }
636 if (!json_util::readJson(*searchSettings, asyncResp->res,
637 "BaseDistinguishedNames", baseDNList,
638 "UsernameAttribute", userNameAttribute,
639 "GroupsAttribute", groupsAttribute))
640 {
641 return;
642 }
643 }
644 /**
645 * @brief updates the LDAP server address and updates the
646 json response with the new value.
647 * @param serviceAddressList address to be updated.
648 * @param asyncResp pointer to the JSON response
649 * @param ldapServerElementName Type of LDAP
650 server(openLDAP/ActiveDirectory)
651 */
652
653 void handleServiceAddressPatch(
654 const std::vector<std::string>& serviceAddressList,
655 const std::shared_ptr<AsyncResp>& asyncResp,
656 const std::string& ldapServerElementName,
657 const std::string& ldapConfigObject)
658 {
659 crow::connections::systemBus->async_method_call(
660 [asyncResp, ldapServerElementName,
661 serviceAddressList](const boost::system::error_code ec) {
662 if (ec)
663 {
664 BMCWEB_LOG_DEBUG
665 << "Error Occured in updating the service address";
666 messages::internalError(asyncResp->res);
667 return;
668 }
669 std::vector<std::string> modifiedserviceAddressList = {
670 serviceAddressList.front()};
671 asyncResp->res
672 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
673 modifiedserviceAddressList;
674 if ((serviceAddressList).size() > 1)
675 {
676 messages::propertyValueModified(asyncResp->res,
677 "ServiceAddresses",
678 serviceAddressList.front());
679 }
680 BMCWEB_LOG_DEBUG << "Updated the service address";
681 },
682 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
683 ldapConfigInterface, "LDAPServerURI",
684 std::variant<std::string>(serviceAddressList.front()));
685 }
686 /**
687 * @brief updates the LDAP Bind DN and updates the
688 json response with the new value.
689 * @param username name of the user which needs to be updated.
690 * @param asyncResp pointer to the JSON response
691 * @param ldapServerElementName Type of LDAP
692 server(openLDAP/ActiveDirectory)
693 */
694
695 void handleUserNamePatch(const std::string& username,
696 const std::shared_ptr<AsyncResp>& asyncResp,
697 const std::string& ldapServerElementName,
698 const std::string& ldapConfigObject)
699 {
700 crow::connections::systemBus->async_method_call(
701 [asyncResp, username,
702 ldapServerElementName](const boost::system::error_code ec) {
703 if (ec)
704 {
705 BMCWEB_LOG_DEBUG
706 << "Error occured in updating the username";
707 messages::internalError(asyncResp->res);
708 return;
709 }
710 asyncResp->res.jsonValue[ldapServerElementName]
711 ["Authentication"]["Username"] =
712 username;
713 BMCWEB_LOG_DEBUG << "Updated the username";
714 },
715 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
716 ldapConfigInterface, "LDAPBindDN",
717 std::variant<std::string>(username));
718 }
719
720 /**
721 * @brief updates the LDAP password
722 * @param password : ldap password which needs to be updated.
723 * @param asyncResp pointer to the JSON response
724 * @param ldapServerElementName Type of LDAP
725 * server(openLDAP/ActiveDirectory)
726 */
727
728 void handlePasswordPatch(const std::string& password,
729 const std::shared_ptr<AsyncResp>& asyncResp,
730 const std::string& ldapServerElementName,
731 const std::string& ldapConfigObject)
732 {
733 crow::connections::systemBus->async_method_call(
734 [asyncResp, password,
735 ldapServerElementName](const boost::system::error_code ec) {
736 if (ec)
737 {
738 BMCWEB_LOG_DEBUG
739 << "Error occured in updating the password";
740 messages::internalError(asyncResp->res);
741 return;
742 }
743 asyncResp->res.jsonValue[ldapServerElementName]
744 ["Authentication"]["Password"] = "";
745 BMCWEB_LOG_DEBUG << "Updated the password";
746 },
747 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
748 ldapConfigInterface, "LDAPBindDNPassword",
749 std::variant<std::string>(password));
750 }
751
752 /**
753 * @brief updates the LDAP BaseDN and updates the
754 json response with the new value.
755 * @param baseDNList baseDN list which needs to be updated.
756 * @param asyncResp pointer to the JSON response
757 * @param ldapServerElementName Type of LDAP
758 server(openLDAP/ActiveDirectory)
759 */
760
761 void handleBaseDNPatch(const std::vector<std::string>& baseDNList,
762 const std::shared_ptr<AsyncResp>& asyncResp,
763 const std::string& ldapServerElementName,
764 const std::string& ldapConfigObject)
765 {
766 crow::connections::systemBus->async_method_call(
767 [asyncResp, baseDNList,
768 ldapServerElementName](const boost::system::error_code ec) {
769 if (ec)
770 {
771 BMCWEB_LOG_DEBUG << "Error Occured in Updating the base DN";
772 messages::internalError(asyncResp->res);
773 return;
774 }
775 auto& serverTypeJson =
776 asyncResp->res.jsonValue[ldapServerElementName];
777 auto& searchSettingsJson =
778 serverTypeJson["LDAPService"]["SearchSettings"];
779 std::vector<std::string> modifiedBaseDNList = {
780 baseDNList.front()};
781 searchSettingsJson["BaseDistinguishedNames"] =
782 modifiedBaseDNList;
783 if (baseDNList.size() > 1)
784 {
785 messages::propertyValueModified(asyncResp->res,
786 "BaseDistinguishedNames",
787 baseDNList.front());
788 }
789 BMCWEB_LOG_DEBUG << "Updated the base DN";
790 },
791 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
792 ldapConfigInterface, "LDAPBaseDN",
793 std::variant<std::string>(baseDNList.front()));
794 }
795 /**
796 * @brief updates the LDAP user name attribute and updates the
797 json response with the new value.
798 * @param userNameAttribute attribute to be updated.
799 * @param asyncResp pointer to the JSON response
800 * @param ldapServerElementName Type of LDAP
801 server(openLDAP/ActiveDirectory)
802 */
803
804 void handleUserNameAttrPatch(const std::string& userNameAttribute,
805 const std::shared_ptr<AsyncResp>& asyncResp,
806 const std::string& ldapServerElementName,
807 const std::string& ldapConfigObject)
808 {
809 crow::connections::systemBus->async_method_call(
810 [asyncResp, userNameAttribute,
811 ldapServerElementName](const boost::system::error_code ec) {
812 if (ec)
813 {
814 BMCWEB_LOG_DEBUG << "Error Occured in Updating the "
815 "username attribute";
816 messages::internalError(asyncResp->res);
817 return;
818 }
819 auto& serverTypeJson =
820 asyncResp->res.jsonValue[ldapServerElementName];
821 auto& searchSettingsJson =
822 serverTypeJson["LDAPService"]["SearchSettings"];
823 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
824 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
825 },
826 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
827 ldapConfigInterface, "UserNameAttribute",
828 std::variant<std::string>(userNameAttribute));
829 }
830 /**
831 * @brief updates the LDAP group attribute and updates the
832 json response with the new value.
833 * @param groupsAttribute attribute to be updated.
834 * @param asyncResp pointer to the JSON response
835 * @param ldapServerElementName Type of LDAP
836 server(openLDAP/ActiveDirectory)
837 */
838
839 void handleGroupNameAttrPatch(const std::string& groupsAttribute,
840 const std::shared_ptr<AsyncResp>& asyncResp,
841 const std::string& ldapServerElementName,
842 const std::string& ldapConfigObject)
843 {
844 crow::connections::systemBus->async_method_call(
845 [asyncResp, groupsAttribute,
846 ldapServerElementName](const boost::system::error_code ec) {
847 if (ec)
848 {
849 BMCWEB_LOG_DEBUG << "Error Occured in Updating the "
850 "groupname attribute";
851 messages::internalError(asyncResp->res);
852 return;
853 }
854 auto& serverTypeJson =
855 asyncResp->res.jsonValue[ldapServerElementName];
856 auto& searchSettingsJson =
857 serverTypeJson["LDAPService"]["SearchSettings"];
858 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
859 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
860 },
861 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
862 ldapConfigInterface, "GroupNameAttribute",
863 std::variant<std::string>(groupsAttribute));
864 }
865 /**
866 * @brief updates the LDAP service enable and updates the
867 json response with the new value.
868 * @param input JSON data.
869 * @param asyncResp pointer to the JSON response
870 * @param ldapServerElementName Type of LDAP
871 server(openLDAP/ActiveDirectory)
872 */
873
874 void handleServiceEnablePatch(bool serviceEnabled,
875 const std::shared_ptr<AsyncResp>& asyncResp,
876 const std::string& ldapServerElementName,
877 const std::string& ldapConfigObject)
878 {
879 crow::connections::systemBus->async_method_call(
880 [asyncResp, serviceEnabled,
881 ldapServerElementName](const boost::system::error_code ec) {
882 if (ec)
883 {
884 BMCWEB_LOG_DEBUG
885 << "Error Occured in Updating the service enable";
886 messages::internalError(asyncResp->res);
887 return;
888 }
889 asyncResp->res
890 .jsonValue[ldapServerElementName]["ServiceEnabled"] =
891 serviceEnabled;
892 BMCWEB_LOG_DEBUG << "Updated Service enable = "
893 << serviceEnabled;
894 },
895 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
896 ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
897 }
898
Zbigniew Kurzynski78158632019-11-05 12:57:37 +0100899 void handleAuthMethodsPatch(nlohmann::json& input,
900 const std::shared_ptr<AsyncResp>& asyncResp)
901 {
902 std::optional<bool> basicAuth;
903 std::optional<bool> cookie;
904 std::optional<bool> sessionToken;
905 std::optional<bool> xToken;
906
907 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
908 "Cookie", cookie, "SessionToken", sessionToken,
909 "XToken", xToken))
910 {
911 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
912 return;
913 }
914
915 // Make a copy of methods configuration
916 crow::persistent_data::AuthConfigMethods authMethodsConfig =
917 crow::persistent_data::SessionStore::getInstance()
918 .getAuthMethodsConfig();
919
920 if (basicAuth)
921 {
922 authMethodsConfig.basic = *basicAuth;
923 }
924
925 if (cookie)
926 {
927 authMethodsConfig.cookie = *cookie;
928 }
929
930 if (sessionToken)
931 {
932 authMethodsConfig.sessionToken = *sessionToken;
933 }
934
935 if (xToken)
936 {
937 authMethodsConfig.xtoken = *xToken;
938 }
939
940 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
941 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken)
942 {
943 // Do not allow user to disable everything
944 messages::actionNotSupported(asyncResp->res,
945 "of disabling all available methods");
946 return;
947 }
948
949 crow::persistent_data::SessionStore::getInstance()
950 .updateAuthMethodsConfig(authMethodsConfig);
951 // Save configuration immediately
952 app.template getMiddleware<crow::persistent_data::Middleware>()
953 .writeData();
954
955 messages::success(asyncResp->res);
956 }
957
Ratan Gupta8a07d282019-03-16 08:33:47 +0530958 /**
959 * @brief Get the required values from the given JSON, validates the
960 * value and create the LDAP config object.
961 * @param input JSON data
962 * @param asyncResp pointer to the JSON response
963 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
964 */
965
966 void handleLDAPPatch(nlohmann::json& input,
967 const std::shared_ptr<AsyncResp>& asyncResp,
968 const crow::Request& req,
969 const std::vector<std::string>& params,
970 const std::string& serverType)
971 {
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530972 std::string dbusObjectPath;
973 if (serverType == "ActiveDirectory")
974 {
975 dbusObjectPath = ADConfigObject;
976 }
977 else if (serverType == "LDAP")
978 {
979 dbusObjectPath = ldapConfigObject;
980 }
981
Ratan Gupta8a07d282019-03-16 08:33:47 +0530982 std::optional<nlohmann::json> authentication;
983 std::optional<nlohmann::json> ldapService;
984 std::optional<std::string> accountProviderType;
985 std::optional<std::vector<std::string>> serviceAddressList;
986 std::optional<bool> serviceEnabled;
987 std::optional<std::vector<std::string>> baseDNList;
988 std::optional<std::string> userNameAttribute;
989 std::optional<std::string> groupsAttribute;
990 std::optional<std::string> userName;
991 std::optional<std::string> password;
Ratan Gupta06785242019-07-26 22:30:16 +0530992 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530993
994 if (!json_util::readJson(input, asyncResp->res, "Authentication",
995 authentication, "LDAPService", ldapService,
996 "ServiceAddresses", serviceAddressList,
997 "AccountProviderType", accountProviderType,
Ratan Gupta06785242019-07-26 22:30:16 +0530998 "ServiceEnabled", serviceEnabled,
999 "RemoteRoleMapping", remoteRoleMapData))
Ratan Gupta8a07d282019-03-16 08:33:47 +05301000 {
1001 return;
1002 }
1003
1004 if (authentication)
1005 {
1006 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1007 password);
1008 }
1009 if (ldapService)
1010 {
1011 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1012 userNameAttribute, groupsAttribute);
1013 }
1014 if (accountProviderType)
1015 {
1016 messages::propertyNotWritable(asyncResp->res,
1017 "AccountProviderType");
1018 }
1019 if (serviceAddressList)
1020 {
1021 if ((*serviceAddressList).size() == 0)
1022 {
1023 messages::propertyValueNotInList(asyncResp->res, "[]",
1024 "ServiceAddress");
1025 return;
1026 }
1027 }
1028 if (baseDNList)
1029 {
1030 if ((*baseDNList).size() == 0)
1031 {
1032 messages::propertyValueNotInList(asyncResp->res, "[]",
1033 "BaseDistinguishedNames");
1034 return;
1035 }
1036 }
1037
1038 // nothing to update, then return
1039 if (!userName && !password && !serviceAddressList && !baseDNList &&
Ratan Gupta06785242019-07-26 22:30:16 +05301040 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1041 !remoteRoleMapData)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301042 {
1043 return;
1044 }
1045
1046 // Get the existing resource first then keep modifying
1047 // whenever any property gets updated.
Ratan Guptaab828d72019-04-22 14:18:33 +05301048 getLDAPConfigData(serverType, [this, asyncResp, userName, password,
1049 baseDNList, userNameAttribute,
1050 groupsAttribute, accountProviderType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301051 serviceAddressList, serviceEnabled,
Ratan Gupta06785242019-07-26 22:30:16 +05301052 dbusObjectPath, remoteRoleMapData](
Ratan Guptaab828d72019-04-22 14:18:33 +05301053 bool success, LDAPConfigData confData,
1054 const std::string& serverType) {
1055 if (!success)
1056 {
1057 messages::internalError(asyncResp->res);
1058 return;
1059 }
1060 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverType);
1061 if (confData.serviceEnabled)
1062 {
1063 // Disable the service first and update the rest of
1064 // the properties.
1065 handleServiceEnablePatch(false, asyncResp, serverType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301066 dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +05301067 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301068
Ratan Guptaab828d72019-04-22 14:18:33 +05301069 if (serviceAddressList)
1070 {
1071 handleServiceAddressPatch(*serviceAddressList, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301072 serverType, dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +05301073 }
1074 if (userName)
1075 {
1076 handleUserNamePatch(*userName, asyncResp, serverType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301077 dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +05301078 }
1079 if (password)
1080 {
1081 handlePasswordPatch(*password, asyncResp, serverType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301082 dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +05301083 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301084
Ratan Guptaab828d72019-04-22 14:18:33 +05301085 if (baseDNList)
1086 {
1087 handleBaseDNPatch(*baseDNList, asyncResp, serverType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301088 dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +05301089 }
1090 if (userNameAttribute)
1091 {
1092 handleUserNameAttrPatch(*userNameAttribute, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301093 serverType, dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +05301094 }
1095 if (groupsAttribute)
1096 {
1097 handleGroupNameAttrPatch(*groupsAttribute, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301098 serverType, dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +05301099 }
1100 if (serviceEnabled)
1101 {
1102 // if user has given the value as true then enable
1103 // the service. if user has given false then no-op
1104 // as service is already stopped.
1105 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301106 {
Ratan Guptaab828d72019-04-22 14:18:33 +05301107 handleServiceEnablePatch(*serviceEnabled, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301108 serverType, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301109 }
Ratan Guptaab828d72019-04-22 14:18:33 +05301110 }
1111 else
1112 {
1113 // if user has not given the service enabled value
1114 // then revert it to the same state as it was
1115 // before.
1116 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301117 serverType, dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +05301118 }
Ratan Gupta06785242019-07-26 22:30:16 +05301119
1120 if (remoteRoleMapData)
1121 {
1122 std::vector<nlohmann::json> remoteRoleMap =
1123 std::move(*remoteRoleMapData);
1124
1125 handleRoleMapPatch(asyncResp, confData.groupRoleList,
1126 serverType, remoteRoleMap);
1127 }
Ratan Guptaab828d72019-04-22 14:18:33 +05301128 });
Ratan Gupta8a07d282019-03-16 08:33:47 +05301129 }
Ed Tanousd4b54432019-07-17 22:51:55 +00001130
Ed Tanous1abe55e2018-09-05 08:30:59 -07001131 void doGet(crow::Response& res, const crow::Request& req,
1132 const std::vector<std::string>& params) override
1133 {
Zbigniew Kurzynski78158632019-11-05 12:57:37 +01001134 const crow::persistent_data::AuthConfigMethods& authMethodsConfig =
1135 crow::persistent_data::SessionStore::getInstance()
1136 .getAuthMethodsConfig();
1137
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301138 auto asyncResp = std::make_shared<AsyncResp>(res);
1139 res.jsonValue = {
1140 {"@odata.context", "/redfish/v1/"
1141 "$metadata#AccountService.AccountService"},
1142 {"@odata.id", "/redfish/v1/AccountService"},
1143 {"@odata.type", "#AccountService."
Marri Devender Rao37cce912019-02-20 01:05:22 -06001144 "v1_4_0.AccountService"},
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301145 {"Id", "AccountService"},
1146 {"Name", "Account Service"},
1147 {"Description", "Account Service"},
1148 {"ServiceEnabled", true},
AppaRao Puli343ff2e2019-03-24 00:42:13 +05301149 {"MaxPasswordLength", 20},
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301150 {"Accounts",
1151 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
Marri Devender Rao37cce912019-02-20 01:05:22 -06001152 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
Zbigniew Kurzynski78158632019-11-05 12:57:37 +01001153 {"Oem",
1154 {{"OpenBMC",
1155 {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
1156 {"AuthMethods",
1157 {
1158 {"BasicAuth", authMethodsConfig.basic},
1159 {"SessionToken", authMethodsConfig.sessionToken},
1160 {"XToken", authMethodsConfig.xtoken},
1161 {"Cookie", authMethodsConfig.cookie},
1162 }}}}}},
Marri Devender Rao37cce912019-02-20 01:05:22 -06001163 {"LDAP",
1164 {{"Certificates",
1165 {{"@odata.id",
1166 "/redfish/v1/AccountService/LDAP/Certificates"}}}}}};
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301167 crow::connections::systemBus->async_method_call(
1168 [asyncResp](
1169 const boost::system::error_code ec,
1170 const std::vector<std::pair<
Ed Tanousabf2add2019-01-22 16:40:12 -08001171 std::string, std::variant<uint32_t, uint16_t, uint8_t>>>&
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301172 propertiesList) {
1173 if (ec)
1174 {
1175 messages::internalError(asyncResp->res);
1176 return;
1177 }
1178 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1179 << "properties for AccountService";
1180 for (const std::pair<std::string,
Ed Tanousabf2add2019-01-22 16:40:12 -08001181 std::variant<uint32_t, uint16_t, uint8_t>>&
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301182 property : propertiesList)
1183 {
1184 if (property.first == "MinPasswordLength")
1185 {
1186 const uint8_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -08001187 std::get_if<uint8_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301188 if (value != nullptr)
1189 {
1190 asyncResp->res.jsonValue["MinPasswordLength"] =
1191 *value;
1192 }
1193 }
1194 if (property.first == "AccountUnlockTimeout")
1195 {
1196 const uint32_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -08001197 std::get_if<uint32_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301198 if (value != nullptr)
1199 {
1200 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1201 *value;
1202 }
1203 }
1204 if (property.first == "MaxLoginAttemptBeforeLockout")
1205 {
1206 const uint16_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -08001207 std::get_if<uint16_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301208 if (value != nullptr)
1209 {
1210 asyncResp->res
1211 .jsonValue["AccountLockoutThreshold"] = *value;
1212 }
1213 }
1214 }
1215 },
1216 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1217 "org.freedesktop.DBus.Properties", "GetAll",
1218 "xyz.openbmc_project.User.AccountPolicy");
Ratan Gupta6973a582018-12-13 18:25:44 +05301219
Ratan Guptaab828d72019-04-22 14:18:33 +05301220 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
1221 const std::string& ldapType) {
1222 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1223 };
1224
1225 getLDAPConfigData("LDAP", callback);
1226 getLDAPConfigData("ActiveDirectory", callback);
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301227 }
Ratan Gupta6973a582018-12-13 18:25:44 +05301228
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301229 void doPatch(crow::Response& res, const crow::Request& req,
1230 const std::vector<std::string>& params) override
1231 {
1232 auto asyncResp = std::make_shared<AsyncResp>(res);
1233
1234 std::optional<uint32_t> unlockTimeout;
1235 std::optional<uint16_t> lockoutThreshold;
Ratan Gupta19fb6e72019-03-04 13:30:50 +05301236 std::optional<uint16_t> minPasswordLength;
1237 std::optional<uint16_t> maxPasswordLength;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301238 std::optional<nlohmann::json> ldapObject;
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301239 std::optional<nlohmann::json> activeDirectoryObject;
Zbigniew Kurzynski78158632019-11-05 12:57:37 +01001240 std::optional<nlohmann::json> oemObject;
Ratan Gupta19fb6e72019-03-04 13:30:50 +05301241
Zbigniew Kurzynski78158632019-11-05 12:57:37 +01001242 if (!json_util::readJson(
1243 req, res, "AccountLockoutDuration", unlockTimeout,
1244 "AccountLockoutThreshold", lockoutThreshold,
1245 "MaxPasswordLength", maxPasswordLength, "MinPasswordLength",
1246 minPasswordLength, "LDAP", ldapObject, "ActiveDirectory",
1247 activeDirectoryObject, "Oem", oemObject))
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301248 {
1249 return;
1250 }
Ratan Gupta19fb6e72019-03-04 13:30:50 +05301251
1252 if (minPasswordLength)
1253 {
1254 messages::propertyNotWritable(asyncResp->res, "MinPasswordLength");
1255 }
1256
1257 if (maxPasswordLength)
1258 {
1259 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1260 }
1261
Ratan Gupta8a07d282019-03-16 08:33:47 +05301262 if (ldapObject)
1263 {
1264 handleLDAPPatch(*ldapObject, asyncResp, req, params, "LDAP");
1265 }
1266
Zbigniew Kurzynski78158632019-11-05 12:57:37 +01001267 if (std::optional<nlohmann::json> oemOpenBMCObject;
1268 oemObject &&
1269 json_util::readJson(*oemObject, res, "OpenBMC", oemOpenBMCObject))
1270 {
1271 if (std::optional<nlohmann::json> authMethodsObject;
1272 oemOpenBMCObject &&
1273 json_util::readJson(*oemOpenBMCObject, res, "AuthMethods",
1274 authMethodsObject))
1275 {
1276 if (authMethodsObject)
1277 {
1278 handleAuthMethodsPatch(*authMethodsObject, asyncResp);
1279 }
1280 }
1281 }
1282
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301283 if (activeDirectoryObject)
1284 {
1285 handleLDAPPatch(*activeDirectoryObject, asyncResp, req, params,
1286 "ActiveDirectory");
1287 }
1288
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301289 if (unlockTimeout)
1290 {
1291 crow::connections::systemBus->async_method_call(
1292 [asyncResp](const boost::system::error_code ec) {
1293 if (ec)
1294 {
1295 messages::internalError(asyncResp->res);
1296 return;
1297 }
Ratan Guptaadd61332019-02-13 20:49:16 +05301298 messages::success(asyncResp->res);
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301299 },
1300 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1301 "org.freedesktop.DBus.Properties", "Set",
1302 "xyz.openbmc_project.User.AccountPolicy",
Ed Tanousabf2add2019-01-22 16:40:12 -08001303 "AccountUnlockTimeout", std::variant<uint32_t>(*unlockTimeout));
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301304 }
1305 if (lockoutThreshold)
1306 {
1307 crow::connections::systemBus->async_method_call(
1308 [asyncResp](const boost::system::error_code ec) {
1309 if (ec)
1310 {
1311 messages::internalError(asyncResp->res);
1312 return;
1313 }
Ratan Guptaadd61332019-02-13 20:49:16 +05301314 messages::success(asyncResp->res);
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301315 },
1316 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1317 "org.freedesktop.DBus.Properties", "Set",
1318 "xyz.openbmc_project.User.AccountPolicy",
1319 "MaxLoginAttemptBeforeLockout",
Ed Tanousabf2add2019-01-22 16:40:12 -08001320 std::variant<uint16_t>(*lockoutThreshold));
AppaRao Puli3d958bb2018-12-25 12:45:54 +05301321 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001322 }
Zbigniew Kurzynski78158632019-11-05 12:57:37 +01001323
1324 CrowApp& app;
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001325};
Tanousf00032d2018-11-05 01:18:10 -03001326
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001327class AccountsCollection : public Node
1328{
1329 public:
1330 AccountsCollection(CrowApp& app) :
1331 Node(app, "/redfish/v1/AccountService/Accounts/")
1332 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001333 entityPrivileges = {
1334 {boost::beast::http::verb::get,
1335 {{"ConfigureUsers"}, {"ConfigureManager"}}},
1336 {boost::beast::http::verb::head, {{"Login"}}},
1337 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
1338 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
1339 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
1340 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
1341 }
1342
1343 private:
1344 void doGet(crow::Response& res, const crow::Request& req,
1345 const std::vector<std::string>& params) override
1346 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001347 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous0f74e642018-11-12 15:17:05 -08001348 res.jsonValue = {{"@odata.context",
1349 "/redfish/v1/"
1350 "$metadata#ManagerAccountCollection."
1351 "ManagerAccountCollection"},
1352 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
1353 {"@odata.type", "#ManagerAccountCollection."
1354 "ManagerAccountCollection"},
1355 {"Name", "Accounts Collection"},
1356 {"Description", "BMC User Accounts"}};
1357
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001358 crow::connections::systemBus->async_method_call(
1359 [asyncResp](const boost::system::error_code ec,
1360 const ManagedObjectType& users) {
1361 if (ec)
1362 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001363 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001364 return;
1365 }
1366
1367 nlohmann::json& memberArray =
1368 asyncResp->res.jsonValue["Members"];
1369 memberArray = nlohmann::json::array();
1370
1371 asyncResp->res.jsonValue["Members@odata.count"] = users.size();
1372 for (auto& user : users)
1373 {
1374 const std::string& path =
1375 static_cast<const std::string&>(user.first);
1376 std::size_t lastIndex = path.rfind("/");
1377 if (lastIndex == std::string::npos)
1378 {
1379 lastIndex = 0;
1380 }
1381 else
1382 {
1383 lastIndex += 1;
1384 }
1385 memberArray.push_back(
1386 {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
1387 path.substr(lastIndex)}});
1388 }
1389 },
1390 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1391 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1392 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001393 void doPost(crow::Response& res, const crow::Request& req,
1394 const std::vector<std::string>& params) override
1395 {
1396 auto asyncResp = std::make_shared<AsyncResp>(res);
1397
Ed Tanous9712f8a2018-09-21 13:38:49 -07001398 std::string username;
1399 std::string password;
Ed Tanousa24526d2018-12-10 15:17:59 -08001400 std::optional<std::string> roleId("User");
1401 std::optional<bool> enabled = true;
Ed Tanous9712f8a2018-09-21 13:38:49 -07001402 if (!json_util::readJson(req, res, "UserName", username, "Password",
1403 password, "RoleId", roleId, "Enabled",
1404 enabled))
Ed Tanous04ae99e2018-09-20 15:54:36 -07001405 {
1406 return;
1407 }
1408
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -06001409 std::string priv = getPrivilegeFromRoleId(*roleId);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301410 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001411 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001412 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001413 return;
1414 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001415 roleId = priv;
Ed Tanous04ae99e2018-09-20 15:54:36 -07001416
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001417 // Reading AllGroups property
Ed Tanous04ae99e2018-09-20 15:54:36 -07001418 crow::connections::systemBus->async_method_call(
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001419 [asyncResp, username, password{std::move(password)}, roleId,
1420 enabled](const boost::system::error_code ec,
1421 const std::variant<std::vector<std::string>>& allGroups) {
Ed Tanous04ae99e2018-09-20 15:54:36 -07001422 if (ec)
1423 {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001424 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1425 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001426 return;
1427 }
1428
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001429 const std::vector<std::string>* allGroupsList =
1430 std::get_if<std::vector<std::string>>(&allGroups);
1431
1432 if (allGroupsList == nullptr || allGroupsList->empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001433 {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001434 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001435 return;
1436 }
1437
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001438 crow::connections::systemBus->async_method_call(
1439 [asyncResp, username, password{std::move(password)}](
anil kumar appana0d4197e2019-06-13 15:06:23 +00001440 const boost::system::error_code ec,
1441 sdbusplus::message::message& m) {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001442 if (ec)
1443 {
anil kumar appana0d4197e2019-06-13 15:06:23 +00001444 userErrorMessageHandler(m.get_error(), asyncResp,
1445 username, "");
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001446 return;
1447 }
1448
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001449 if (pamUpdatePassword(username, password) !=
1450 PAM_SUCCESS)
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001451 {
1452 // At this point we have a user that's been created,
1453 // but the password set failed.Something is wrong,
1454 // so delete the user that we've already created
1455 crow::connections::systemBus->async_method_call(
anil kumar appana0d4197e2019-06-13 15:06:23 +00001456 [asyncResp,
1457 password](const boost::system::error_code ec) {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001458 if (ec)
1459 {
1460 messages::internalError(asyncResp->res);
1461 return;
1462 }
1463
anil kumar appana0d4197e2019-06-13 15:06:23 +00001464 // If password is invalid
1465 messages::propertyValueFormatError(
1466 asyncResp->res, password, "Password");
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001467 },
1468 "xyz.openbmc_project.User.Manager",
1469 "/xyz/openbmc_project/user/" + username,
1470 "xyz.openbmc_project.Object.Delete", "Delete");
1471
1472 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1473 return;
1474 }
1475
1476 messages::created(asyncResp->res);
1477 asyncResp->res.addHeader(
1478 "Location",
1479 "/redfish/v1/AccountService/Accounts/" + username);
1480 },
1481 "xyz.openbmc_project.User.Manager",
1482 "/xyz/openbmc_project/user",
1483 "xyz.openbmc_project.User.Manager", "CreateUser", username,
1484 *allGroupsList, *roleId, *enabled);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001485 },
1486 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001487 "org.freedesktop.DBus.Properties", "Get",
1488 "xyz.openbmc_project.User.Manager", "AllGroups");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001489 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001490};
1491
1492class ManagerAccount : public Node
1493{
1494 public:
1495 ManagerAccount(CrowApp& app) :
1496 Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
1497 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001498 entityPrivileges = {
1499 {boost::beast::http::verb::get,
1500 {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
1501 {boost::beast::http::verb::head, {{"Login"}}},
1502 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
1503 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
1504 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
1505 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
1506 }
1507
1508 private:
1509 void doGet(crow::Response& res, const crow::Request& req,
1510 const std::vector<std::string>& params) override
1511 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001512
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001513 auto asyncResp = std::make_shared<AsyncResp>(res);
1514
1515 if (params.size() != 1)
1516 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001517 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001518 return;
1519 }
1520
1521 crow::connections::systemBus->async_method_call(
1522 [asyncResp, accountName{std::string(params[0])}](
1523 const boost::system::error_code ec,
1524 const ManagedObjectType& users) {
1525 if (ec)
1526 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001527 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001528 return;
1529 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301530 auto userIt = users.begin();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001531
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301532 for (; userIt != users.end(); userIt++)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001533 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301534 if (boost::ends_with(userIt->first.str, "/" + accountName))
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001535 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301536 break;
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001537 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301538 }
1539 if (userIt == users.end())
1540 {
1541 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1542 accountName);
1543 return;
1544 }
Ayushi Smriti4e68c452019-09-04 14:37:55 +05301545
1546 asyncResp->res.jsonValue = {
1547 {"@odata.context",
1548 "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
1549 {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
1550 {"Name", "User Account"},
1551 {"Description", "User Account"},
1552 {"Password", nullptr}};
1553
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301554 for (const auto& interface : userIt->second)
1555 {
1556 if (interface.first ==
1557 "xyz.openbmc_project.User.Attributes")
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001558 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301559 for (const auto& property : interface.second)
Ed Tanous65b0dc32018-09-19 16:04:03 -07001560 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301561 if (property.first == "UserEnabled")
Ed Tanous65b0dc32018-09-19 16:04:03 -07001562 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301563 const bool* userEnabled =
Ed Tanousabf2add2019-01-22 16:40:12 -08001564 std::get_if<bool>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301565 if (userEnabled == nullptr)
Ed Tanous65b0dc32018-09-19 16:04:03 -07001566 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301567 BMCWEB_LOG_ERROR
1568 << "UserEnabled wasn't a bool";
1569 messages::internalError(asyncResp->res);
1570 return;
Ed Tanous65b0dc32018-09-19 16:04:03 -07001571 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301572 asyncResp->res.jsonValue["Enabled"] =
1573 *userEnabled;
1574 }
1575 else if (property.first ==
1576 "UserLockedForFailedAttempt")
1577 {
1578 const bool* userLocked =
Ed Tanousabf2add2019-01-22 16:40:12 -08001579 std::get_if<bool>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301580 if (userLocked == nullptr)
1581 {
1582 BMCWEB_LOG_ERROR << "UserLockedForF"
1583 "ailedAttempt "
1584 "wasn't a bool";
1585 messages::internalError(asyncResp->res);
1586 return;
1587 }
1588 asyncResp->res.jsonValue["Locked"] =
1589 *userLocked;
Ratan Gupta24c85422019-01-30 19:41:24 +05301590 asyncResp->res.jsonValue
1591 ["Locked@Redfish.AllowableValues"] = {
Gunnar Mills4d64ce32019-03-29 16:34:56 -05001592 "false"};
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301593 }
1594 else if (property.first == "UserPrivilege")
1595 {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -06001596 const std::string* userPrivPtr =
Ed Tanousabf2add2019-01-22 16:40:12 -08001597 std::get_if<std::string>(&property.second);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -06001598 if (userPrivPtr == nullptr)
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301599 {
1600 BMCWEB_LOG_ERROR
1601 << "UserPrivilege wasn't a "
1602 "string";
1603 messages::internalError(asyncResp->res);
1604 return;
1605 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -06001606 std::string role =
1607 getRoleIdFromPrivilege(*userPrivPtr);
1608 if (role.empty())
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301609 {
1610 BMCWEB_LOG_ERROR << "Invalid user role";
1611 messages::internalError(asyncResp->res);
1612 return;
1613 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -06001614 asyncResp->res.jsonValue["RoleId"] = role;
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301615
1616 asyncResp->res.jsonValue["Links"]["Role"] = {
1617 {"@odata.id", "/redfish/v1/AccountService/"
1618 "Roles/" +
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -06001619 role}};
Ed Tanous65b0dc32018-09-19 16:04:03 -07001620 }
1621 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001622 }
1623 }
1624
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301625 asyncResp->res.jsonValue["@odata.id"] =
1626 "/redfish/v1/AccountService/Accounts/" + accountName;
1627 asyncResp->res.jsonValue["Id"] = accountName;
1628 asyncResp->res.jsonValue["UserName"] = accountName;
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001629 },
1630 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1631 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1632 }
Ed Tanousa8408792018-09-05 16:08:38 -07001633
1634 void doPatch(crow::Response& res, const crow::Request& req,
1635 const std::vector<std::string>& params) override
1636 {
1637 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousa8408792018-09-05 16:08:38 -07001638 if (params.size() != 1)
1639 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001640 messages::internalError(asyncResp->res);
Ed Tanousa8408792018-09-05 16:08:38 -07001641 return;
1642 }
1643
Ed Tanousa24526d2018-12-10 15:17:59 -08001644 std::optional<std::string> newUserName;
1645 std::optional<std::string> password;
1646 std::optional<bool> enabled;
1647 std::optional<std::string> roleId;
Ratan Gupta24c85422019-01-30 19:41:24 +05301648 std::optional<bool> locked;
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301649 if (!json_util::readJson(req, res, "UserName", newUserName, "Password",
Ratan Gupta24c85422019-01-30 19:41:24 +05301650 password, "RoleId", roleId, "Enabled", enabled,
1651 "Locked", locked))
Ed Tanousa8408792018-09-05 16:08:38 -07001652 {
1653 return;
1654 }
1655
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301656 const std::string& username = params[0];
Ed Tanousa8408792018-09-05 16:08:38 -07001657
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001658 // if user name is not provided in the patch method or if it
1659 // matches the user name in the URI, then we are treating it as updating
1660 // user properties other then username. If username provided doesn't
1661 // match the URI, then we are treating this as user rename request.
1662 if (!newUserName || (newUserName.value() == username))
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301663 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301664 updateUserProperties(asyncResp, username, password, enabled, roleId,
1665 locked);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301666 return;
1667 }
1668 else
1669 {
1670 crow::connections::systemBus->async_method_call(
1671 [this, asyncResp, username, password(std::move(password)),
1672 roleId(std::move(roleId)), enabled(std::move(enabled)),
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001673 newUser{std::string(*newUserName)},
1674 locked(std::move(locked))](const boost::system::error_code ec,
1675 sdbusplus::message::message& m) {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301676 if (ec)
Ed Tanousa8408792018-09-05 16:08:38 -07001677 {
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001678 userErrorMessageHandler(m.get_error(), asyncResp,
1679 newUser, username);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301680 return;
1681 }
1682
1683 updateUserProperties(asyncResp, newUser, password, enabled,
Ratan Gupta24c85422019-01-30 19:41:24 +05301684 roleId, locked);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301685 },
1686 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1687 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1688 *newUserName);
1689 }
1690 }
1691
1692 void updateUserProperties(std::shared_ptr<AsyncResp> asyncResp,
1693 const std::string& username,
Ed Tanousa24526d2018-12-10 15:17:59 -08001694 std::optional<std::string> password,
1695 std::optional<bool> enabled,
Ratan Gupta24c85422019-01-30 19:41:24 +05301696 std::optional<std::string> roleId,
1697 std::optional<bool> locked)
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301698 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301699 std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
1700 dbus::utility::escapePathForDbus(dbusObjectPath);
1701
Ratan Gupta22c33712019-05-03 21:50:28 +05301702 dbus::utility::checkDbusPathExists(
Ratan Gupta24c85422019-01-30 19:41:24 +05301703 dbusObjectPath,
1704 [dbusObjectPath(std::move(dbusObjectPath)), username,
1705 password(std::move(password)), roleId(std::move(roleId)),
1706 enabled(std::move(enabled)), locked(std::move(locked)),
1707 asyncResp{std::move(asyncResp)}](int rc) {
1708 if (!rc)
1709 {
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001710 messages::resourceNotFound(
1711 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
1712 username);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301713 return;
Ratan Gupta24c85422019-01-30 19:41:24 +05301714 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001715
1716 if (password)
1717 {
1718 int retval = pamUpdatePassword(username, *password);
1719
1720 if (retval == PAM_USER_UNKNOWN)
1721 {
1722 messages::resourceNotFound(
1723 asyncResp->res,
1724 "#ManagerAccount.v1_0_3.ManagerAccount", username);
1725 }
1726 else if (retval == PAM_AUTHTOK_ERR)
1727 {
1728 // If password is invalid
1729 messages::propertyValueFormatError(
1730 asyncResp->res, *password, "Password");
1731 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1732 }
1733 else if (retval != PAM_SUCCESS)
1734 {
1735 messages::internalError(asyncResp->res);
1736 return;
1737 }
1738 }
1739
Ratan Gupta24c85422019-01-30 19:41:24 +05301740 if (enabled)
1741 {
1742 crow::connections::systemBus->async_method_call(
1743 [asyncResp](const boost::system::error_code ec) {
1744 if (ec)
1745 {
1746 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1747 << ec;
1748 messages::internalError(asyncResp->res);
1749 return;
1750 }
1751 messages::success(asyncResp->res);
1752 return;
1753 },
1754 "xyz.openbmc_project.User.Manager",
1755 dbusObjectPath.c_str(),
1756 "org.freedesktop.DBus.Properties", "Set",
1757 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1758 std::variant<bool>{*enabled});
1759 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001760
Ratan Gupta24c85422019-01-30 19:41:24 +05301761 if (roleId)
1762 {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -06001763 std::string priv = getPrivilegeFromRoleId(*roleId);
Ratan Gupta24c85422019-01-30 19:41:24 +05301764 if (priv.empty())
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301765 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301766 messages::propertyValueNotInList(asyncResp->res,
1767 *roleId, "RoleId");
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301768 return;
1769 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301770
1771 crow::connections::systemBus->async_method_call(
1772 [asyncResp](const boost::system::error_code ec) {
1773 if (ec)
1774 {
1775 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1776 << ec;
1777 messages::internalError(asyncResp->res);
1778 return;
1779 }
1780 messages::success(asyncResp->res);
1781 },
1782 "xyz.openbmc_project.User.Manager",
1783 dbusObjectPath.c_str(),
1784 "org.freedesktop.DBus.Properties", "Set",
1785 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1786 std::variant<std::string>{priv});
1787 }
1788
1789 if (locked)
1790 {
1791 // admin can unlock the account which is locked by
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -06001792 // successive authentication failures but admin should
1793 // not be allowed to lock an account.
Ratan Gupta24c85422019-01-30 19:41:24 +05301794 if (*locked)
1795 {
1796 messages::propertyValueNotInList(asyncResp->res, "true",
1797 "Locked");
1798 return;
1799 }
1800
1801 crow::connections::systemBus->async_method_call(
1802 [asyncResp](const boost::system::error_code ec) {
1803 if (ec)
1804 {
1805 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1806 << ec;
1807 messages::internalError(asyncResp->res);
1808 return;
1809 }
1810 messages::success(asyncResp->res);
1811 return;
1812 },
1813 "xyz.openbmc_project.User.Manager",
1814 dbusObjectPath.c_str(),
1815 "org.freedesktop.DBus.Properties", "Set",
1816 "xyz.openbmc_project.User.Attributes",
1817 "UserLockedForFailedAttempt",
1818 sdbusplus::message::variant<bool>{*locked});
1819 }
1820 });
Ed Tanousa8408792018-09-05 16:08:38 -07001821 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001822
1823 void doDelete(crow::Response& res, const crow::Request& req,
1824 const std::vector<std::string>& params) override
1825 {
1826 auto asyncResp = std::make_shared<AsyncResp>(res);
1827
1828 if (params.size() != 1)
1829 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001830 messages::internalError(asyncResp->res);
Ed Tanous06e086d2018-09-19 17:19:52 -07001831 return;
1832 }
1833
1834 const std::string userPath = "/xyz/openbmc_project/user/" + params[0];
1835
1836 crow::connections::systemBus->async_method_call(
1837 [asyncResp, username{std::move(params[0])}](
1838 const boost::system::error_code ec) {
1839 if (ec)
1840 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001841 messages::resourceNotFound(
1842 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
1843 username);
Ed Tanous06e086d2018-09-19 17:19:52 -07001844 return;
1845 }
1846
Jason M. Billsf12894f2018-10-09 12:45:45 -07001847 messages::accountRemoved(asyncResp->res);
Ed Tanous06e086d2018-09-19 17:19:52 -07001848 },
1849 "xyz.openbmc_project.User.Manager", userPath,
1850 "xyz.openbmc_project.Object.Delete", "Delete");
1851 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301852};
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001853
Ed Tanous1abe55e2018-09-05 08:30:59 -07001854} // namespace redfish