blob: 061c1a2df990f09f0ec5e5f9c59eccf0a5881826 [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";
30constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
31constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
32constexpr const char* ldapConfigInterface =
33 "xyz.openbmc_project.User.Ldap.Config";
34constexpr const char* ldapCreateInterface =
35 "xyz.openbmc_project.User.Ldap.Create";
36constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
37constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
38constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
39constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
40constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
41constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
42
43struct LDAPConfigData
44{
45 std::string uri{};
46 std::string bindDN{};
47 std::string baseDN{};
48 std::string searchScope{};
49 std::string serverType{};
50 bool serviceEnabled = false;
51 std::string userNameAttribute{};
52 std::string groupAttribute{};
53};
54
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070055using ManagedObjectType = std::vector<std::pair<
56 sdbusplus::message::object_path,
57 boost::container::flat_map<
Ed Tanousabf2add2019-01-22 16:40:12 -080058 std::string, boost::container::flat_map<
59 std::string, std::variant<bool, std::string>>>>>;
Ratan Gupta6973a582018-12-13 18:25:44 +053060using GetObjectType =
61 std::vector<std::pair<std::string, std::vector<std::string>>>;
AppaRao Puli84e12cb2018-10-11 01:28:15 +053062
Adriana Kobylakae29b8c2019-04-24 11:19:18 -050063inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053064{
65 if (role == "priv-admin")
66 {
67 return "Administrator";
68 }
69 else if (role == "priv-callback")
70 {
71 return "Callback";
72 }
73 else if (role == "priv-user")
74 {
75 return "User";
76 }
77 else if (role == "priv-operator")
78 {
79 return "Operator";
80 }
81 return "";
82}
Adriana Kobylakae29b8c2019-04-24 11:19:18 -050083inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053084{
85 if (role == "Administrator")
86 {
87 return "priv-admin";
88 }
89 else if (role == "Callback")
90 {
91 return "priv-callback";
92 }
93 else if (role == "User")
94 {
95 return "priv-user";
96 }
97 else if (role == "Operator")
98 {
99 return "priv-operator";
100 }
101 return "";
102}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700103
Ratan Gupta6973a582018-12-13 18:25:44 +0530104void parseLDAPConfigData(nlohmann::json& json_response,
105 const LDAPConfigData& confData)
106{
107 std::string service = "LDAPService";
108 json_response["LDAP"] = {
109 {"AccountProviderType", service},
Ratan Gupta6973a582018-12-13 18:25:44 +0530110 {"ServiceEnabled", confData.serviceEnabled},
111 {"ServiceAddresses", nlohmann::json::array({confData.uri})},
112 {"Authentication",
113 {{"AuthenticationType", "UsernameAndPassword"},
Ratan Gupta6973a582018-12-13 18:25:44 +0530114 {"Username", confData.bindDN},
115 {"Password", nullptr}}},
116 {"LDAPService",
117 {{"SearchSettings",
118 {{"BaseDistinguishedNames",
119 nlohmann::json::array({confData.baseDN})},
120 {"UsernameAttribute", confData.userNameAttribute},
121 {"GroupsAttribute", confData.groupAttribute}}}}},
122 };
123}
124
125/**
126 * Function that retrieves all properties for LDAP config object
127 * into JSON
128 */
129template <typename CallbackFunc>
130inline void getLDAPConfigData(const std::string& ldapType,
131 CallbackFunc&& callback)
132{
133 auto getConfig = [callback,
134 ldapType](const boost::system::error_code error_code,
135 const ManagedObjectType& ldapObjects) {
136 LDAPConfigData confData{};
137 if (error_code)
138 {
139 callback(false, confData);
140 BMCWEB_LOG_ERROR << "D-Bus responses error: " << error_code;
141 return;
142 }
143 std::string ldapConfigObjectStr = std::string(ldapConfigObject);
144 std::string ldapEnableInterfaceStr = std::string(ldapEnableInterface);
145 std::string ldapConfigInterfaceStr = std::string(ldapConfigInterface);
146 for (const auto& object : ldapObjects)
147 {
148 if (object.first == ldapConfigObjectStr)
149 {
150 for (const auto& interface : object.second)
151 {
152 if (interface.first == ldapEnableInterfaceStr)
153 {
154 // rest of the properties are string.
155 for (const auto& property : interface.second)
156 {
157 if (property.first == "Enabled")
158 {
159 const bool* value =
160 std::get_if<bool>(&property.second);
161 if (value == nullptr)
162 {
163 continue;
164 }
165 confData.serviceEnabled = *value;
166 break;
167 }
168 }
169 }
170 else if (interface.first == ldapConfigInterfaceStr)
171 {
172
173 for (const auto& property : interface.second)
174 {
175 const std::string* value =
176 std::get_if<std::string>(&property.second);
177 if (value == nullptr)
178 {
179 continue;
180 }
181 if (property.first == "LDAPServerURI")
182 {
183 confData.uri = *value;
184 }
185 else if (property.first == "LDAPBindDN")
186 {
187 confData.bindDN = *value;
188 }
189 else if (property.first == "LDAPBaseDN")
190 {
191 confData.baseDN = *value;
192 }
193 else if (property.first == "LDAPSearchScope")
194 {
195 confData.searchScope = *value;
196 }
197 else if (property.first == "LDAPType")
198 {
199 confData.serverType = *value;
200 }
201 else if (property.first == "GroupNameAttribute")
202 {
203 confData.groupAttribute = *value;
204 }
205 else if (property.first == "UserNameAttribute")
206 {
207 confData.userNameAttribute = *value;
208 }
209 }
210 }
211 }
212
213 callback(true, confData);
214 break;
215 }
216 }
217 };
218 auto getServiceName = [callback, getConfig(std::move(getConfig))](
219 const boost::system::error_code ec,
220 const GetObjectType& resp) {
221 LDAPConfigData confData{};
222 if (ec || resp.empty())
223 {
224 BMCWEB_LOG_ERROR
225 << "DBUS response error during getting of service name: " << ec;
226 callback(false, confData);
227 return;
228 }
229 std::string service = resp.begin()->first;
230 crow::connections::systemBus->async_method_call(
231 std::move(getConfig), service, ldapRootObject, dbusObjManagerIntf,
232 "GetManagedObjects");
233 };
234
235 const std::array<std::string, 2> interfaces = {ldapEnableInterface,
236 ldapConfigInterface};
237
238 crow::connections::systemBus->async_method_call(
239 std::move(getServiceName), mapperBusName, mapperObjectPath, mapperIntf,
240 "GetObject", ldapConfigObject, interfaces);
241}
242
Ed Tanous1abe55e2018-09-05 08:30:59 -0700243class AccountService : public Node
244{
245 public:
246 AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/")
247 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 entityPrivileges = {
249 {boost::beast::http::verb::get,
250 {{"ConfigureUsers"}, {"ConfigureManager"}}},
251 {boost::beast::http::verb::head, {{"Login"}}},
252 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
253 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
254 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
255 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
256 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +0100257
Ed Tanous1abe55e2018-09-05 08:30:59 -0700258 private:
Ratan Gupta8a07d282019-03-16 08:33:47 +0530259 /**
260 * @brief parses the authentication section under the LDAP
261 * @param input JSON data
262 * @param asyncResp pointer to the JSON response
263 * @param userName userName to be filled from the given JSON.
264 * @param password password to be filled from the given JSON.
265 */
266 void
267 parseLDAPAuthenticationJson(nlohmann::json input,
268 const std::shared_ptr<AsyncResp>& asyncResp,
269 std::optional<std::string>& username,
270 std::optional<std::string>& password)
271 {
272 std::optional<std::string> authType;
273
274 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
275 authType, "Username", username, "Password",
276 password))
277 {
278 return;
279 }
280 if (!authType)
281 {
282 return;
283 }
284 if (*authType != "UsernameAndPassword")
285 {
286 messages::propertyValueNotInList(asyncResp->res, *authType,
287 "AuthenticationType");
288 return;
289 }
290 }
291 /**
292 * @brief parses the LDAPService section under the LDAP
293 * @param input JSON data
294 * @param asyncResp pointer to the JSON response
295 * @param baseDNList baseDN to be filled from the given JSON.
296 * @param userNameAttribute userName to be filled from the given JSON.
297 * @param groupaAttribute password to be filled from the given JSON.
298 */
299
300 void parseLDAPServiceJson(
301 nlohmann::json input, const std::shared_ptr<AsyncResp>& asyncResp,
302 std::optional<std::vector<std::string>>& baseDNList,
303 std::optional<std::string>& userNameAttribute,
304 std::optional<std::string>& groupsAttribute)
305 {
306 std::optional<nlohmann::json> searchSettings;
307
308 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
309 searchSettings))
310 {
311 return;
312 }
313 if (!searchSettings)
314 {
315 return;
316 }
317 if (!json_util::readJson(*searchSettings, asyncResp->res,
318 "BaseDistinguishedNames", baseDNList,
319 "UsernameAttribute", userNameAttribute,
320 "GroupsAttribute", groupsAttribute))
321 {
322 return;
323 }
324 }
325 /**
326 * @brief updates the LDAP server address and updates the
327 json response with the new value.
328 * @param serviceAddressList address to be updated.
329 * @param asyncResp pointer to the JSON response
330 * @param ldapServerElementName Type of LDAP
331 server(openLDAP/ActiveDirectory)
332 */
333
334 void handleServiceAddressPatch(
335 const std::vector<std::string>& serviceAddressList,
336 const std::shared_ptr<AsyncResp>& asyncResp,
337 const std::string& ldapServerElementName,
338 const std::string& ldapConfigObject)
339 {
340 crow::connections::systemBus->async_method_call(
341 [asyncResp, ldapServerElementName,
342 serviceAddressList](const boost::system::error_code ec) {
343 if (ec)
344 {
345 BMCWEB_LOG_DEBUG
346 << "Error Occured in updating the service address";
347 messages::internalError(asyncResp->res);
348 return;
349 }
350 std::vector<std::string> modifiedserviceAddressList = {
351 serviceAddressList.front()};
352 asyncResp->res
353 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
354 modifiedserviceAddressList;
355 if ((serviceAddressList).size() > 1)
356 {
357 messages::propertyValueModified(asyncResp->res,
358 "ServiceAddresses",
359 serviceAddressList.front());
360 }
361 BMCWEB_LOG_DEBUG << "Updated the service address";
362 },
363 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
364 ldapConfigInterface, "LDAPServerURI",
365 std::variant<std::string>(serviceAddressList.front()));
366 }
367 /**
368 * @brief updates the LDAP Bind DN and updates the
369 json response with the new value.
370 * @param username name of the user which needs to be updated.
371 * @param asyncResp pointer to the JSON response
372 * @param ldapServerElementName Type of LDAP
373 server(openLDAP/ActiveDirectory)
374 */
375
376 void handleUserNamePatch(const std::string& username,
377 const std::shared_ptr<AsyncResp>& asyncResp,
378 const std::string& ldapServerElementName,
379 const std::string& ldapConfigObject)
380 {
381 crow::connections::systemBus->async_method_call(
382 [asyncResp, username,
383 ldapServerElementName](const boost::system::error_code ec) {
384 if (ec)
385 {
386 BMCWEB_LOG_DEBUG
387 << "Error occured in updating the username";
388 messages::internalError(asyncResp->res);
389 return;
390 }
391 asyncResp->res.jsonValue[ldapServerElementName]
392 ["Authentication"]["Username"] =
393 username;
394 BMCWEB_LOG_DEBUG << "Updated the username";
395 },
396 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
397 ldapConfigInterface, "LDAPBindDN",
398 std::variant<std::string>(username));
399 }
400
401 /**
402 * @brief updates the LDAP password
403 * @param password : ldap password which needs to be updated.
404 * @param asyncResp pointer to the JSON response
405 * @param ldapServerElementName Type of LDAP
406 * server(openLDAP/ActiveDirectory)
407 */
408
409 void handlePasswordPatch(const std::string& password,
410 const std::shared_ptr<AsyncResp>& asyncResp,
411 const std::string& ldapServerElementName,
412 const std::string& ldapConfigObject)
413 {
414 crow::connections::systemBus->async_method_call(
415 [asyncResp, password,
416 ldapServerElementName](const boost::system::error_code ec) {
417 if (ec)
418 {
419 BMCWEB_LOG_DEBUG
420 << "Error occured in updating the password";
421 messages::internalError(asyncResp->res);
422 return;
423 }
424 asyncResp->res.jsonValue[ldapServerElementName]
425 ["Authentication"]["Password"] = "";
426 BMCWEB_LOG_DEBUG << "Updated the password";
427 },
428 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
429 ldapConfigInterface, "LDAPBindDNPassword",
430 std::variant<std::string>(password));
431 }
432
433 /**
434 * @brief updates the LDAP BaseDN and updates the
435 json response with the new value.
436 * @param baseDNList baseDN list which needs to be updated.
437 * @param asyncResp pointer to the JSON response
438 * @param ldapServerElementName Type of LDAP
439 server(openLDAP/ActiveDirectory)
440 */
441
442 void handleBaseDNPatch(const std::vector<std::string>& baseDNList,
443 const std::shared_ptr<AsyncResp>& asyncResp,
444 const std::string& ldapServerElementName,
445 const std::string& ldapConfigObject)
446 {
447 crow::connections::systemBus->async_method_call(
448 [asyncResp, baseDNList,
449 ldapServerElementName](const boost::system::error_code ec) {
450 if (ec)
451 {
452 BMCWEB_LOG_DEBUG << "Error Occured in Updating the base DN";
453 messages::internalError(asyncResp->res);
454 return;
455 }
456 auto& serverTypeJson =
457 asyncResp->res.jsonValue[ldapServerElementName];
458 auto& searchSettingsJson =
459 serverTypeJson["LDAPService"]["SearchSettings"];
460 std::vector<std::string> modifiedBaseDNList = {
461 baseDNList.front()};
462 searchSettingsJson["BaseDistinguishedNames"] =
463 modifiedBaseDNList;
464 if (baseDNList.size() > 1)
465 {
466 messages::propertyValueModified(asyncResp->res,
467 "BaseDistinguishedNames",
468 baseDNList.front());
469 }
470 BMCWEB_LOG_DEBUG << "Updated the base DN";
471 },
472 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
473 ldapConfigInterface, "LDAPBaseDN",
474 std::variant<std::string>(baseDNList.front()));
475 }
476 /**
477 * @brief updates the LDAP user name attribute and updates the
478 json response with the new value.
479 * @param userNameAttribute attribute to be updated.
480 * @param asyncResp pointer to the JSON response
481 * @param ldapServerElementName Type of LDAP
482 server(openLDAP/ActiveDirectory)
483 */
484
485 void handleUserNameAttrPatch(const std::string& userNameAttribute,
486 const std::shared_ptr<AsyncResp>& asyncResp,
487 const std::string& ldapServerElementName,
488 const std::string& ldapConfigObject)
489 {
490 crow::connections::systemBus->async_method_call(
491 [asyncResp, userNameAttribute,
492 ldapServerElementName](const boost::system::error_code ec) {
493 if (ec)
494 {
495 BMCWEB_LOG_DEBUG << "Error Occured in Updating the "
496 "username attribute";
497 messages::internalError(asyncResp->res);
498 return;
499 }
500 auto& serverTypeJson =
501 asyncResp->res.jsonValue[ldapServerElementName];
502 auto& searchSettingsJson =
503 serverTypeJson["LDAPService"]["SearchSettings"];
504 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
505 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
506 },
507 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
508 ldapConfigInterface, "UserNameAttribute",
509 std::variant<std::string>(userNameAttribute));
510 }
511 /**
512 * @brief updates the LDAP group attribute and updates the
513 json response with the new value.
514 * @param groupsAttribute attribute to be updated.
515 * @param asyncResp pointer to the JSON response
516 * @param ldapServerElementName Type of LDAP
517 server(openLDAP/ActiveDirectory)
518 */
519
520 void handleGroupNameAttrPatch(const std::string& groupsAttribute,
521 const std::shared_ptr<AsyncResp>& asyncResp,
522 const std::string& ldapServerElementName,
523 const std::string& ldapConfigObject)
524 {
525 crow::connections::systemBus->async_method_call(
526 [asyncResp, groupsAttribute,
527 ldapServerElementName](const boost::system::error_code ec) {
528 if (ec)
529 {
530 BMCWEB_LOG_DEBUG << "Error Occured in Updating the "
531 "groupname attribute";
532 messages::internalError(asyncResp->res);
533 return;
534 }
535 auto& serverTypeJson =
536 asyncResp->res.jsonValue[ldapServerElementName];
537 auto& searchSettingsJson =
538 serverTypeJson["LDAPService"]["SearchSettings"];
539 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
540 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
541 },
542 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
543 ldapConfigInterface, "GroupNameAttribute",
544 std::variant<std::string>(groupsAttribute));
545 }
546 /**
547 * @brief updates the LDAP service enable and updates the
548 json response with the new value.
549 * @param input JSON data.
550 * @param asyncResp pointer to the JSON response
551 * @param ldapServerElementName Type of LDAP
552 server(openLDAP/ActiveDirectory)
553 */
554
555 void handleServiceEnablePatch(bool serviceEnabled,
556 const std::shared_ptr<AsyncResp>& asyncResp,
557 const std::string& ldapServerElementName,
558 const std::string& ldapConfigObject)
559 {
560 crow::connections::systemBus->async_method_call(
561 [asyncResp, serviceEnabled,
562 ldapServerElementName](const boost::system::error_code ec) {
563 if (ec)
564 {
565 BMCWEB_LOG_DEBUG
566 << "Error Occured in Updating the service enable";
567 messages::internalError(asyncResp->res);
568 return;
569 }
570 asyncResp->res
571 .jsonValue[ldapServerElementName]["ServiceEnabled"] =
572 serviceEnabled;
573 BMCWEB_LOG_DEBUG << "Updated Service enable = "
574 << serviceEnabled;
575 },
576 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
577 ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
578 }
579
580 /**
581 * @brief Get the required values from the given JSON, validates the
582 * value and create the LDAP config object.
583 * @param input JSON data
584 * @param asyncResp pointer to the JSON response
585 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
586 */
587
588 void handleLDAPPatch(nlohmann::json& input,
589 const std::shared_ptr<AsyncResp>& asyncResp,
590 const crow::Request& req,
591 const std::vector<std::string>& params,
592 const std::string& serverType)
593 {
594 std::optional<nlohmann::json> authentication;
595 std::optional<nlohmann::json> ldapService;
596 std::optional<std::string> accountProviderType;
597 std::optional<std::vector<std::string>> serviceAddressList;
598 std::optional<bool> serviceEnabled;
599 std::optional<std::vector<std::string>> baseDNList;
600 std::optional<std::string> userNameAttribute;
601 std::optional<std::string> groupsAttribute;
602 std::optional<std::string> userName;
603 std::optional<std::string> password;
604
605 if (!json_util::readJson(input, asyncResp->res, "Authentication",
606 authentication, "LDAPService", ldapService,
607 "ServiceAddresses", serviceAddressList,
608 "AccountProviderType", accountProviderType,
609 "ServiceEnabled", serviceEnabled))
610 {
611 return;
612 }
613
614 if (authentication)
615 {
616 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
617 password);
618 }
619 if (ldapService)
620 {
621 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
622 userNameAttribute, groupsAttribute);
623 }
624 if (accountProviderType)
625 {
626 messages::propertyNotWritable(asyncResp->res,
627 "AccountProviderType");
628 }
629 if (serviceAddressList)
630 {
631 if ((*serviceAddressList).size() == 0)
632 {
633 messages::propertyValueNotInList(asyncResp->res, "[]",
634 "ServiceAddress");
635 return;
636 }
637 }
638 if (baseDNList)
639 {
640 if ((*baseDNList).size() == 0)
641 {
642 messages::propertyValueNotInList(asyncResp->res, "[]",
643 "BaseDistinguishedNames");
644 return;
645 }
646 }
647
648 // nothing to update, then return
649 if (!userName && !password && !serviceAddressList && !baseDNList &&
650 !userNameAttribute && !groupsAttribute && !serviceEnabled)
651 {
652 return;
653 }
654
655 // Get the existing resource first then keep modifying
656 // whenever any property gets updated.
657 getLDAPConfigData(
658 serverType,
659 [this, asyncResp, userName, password, baseDNList, userNameAttribute,
660 groupsAttribute, accountProviderType, serviceAddressList,
661 serviceEnabled,
662 serverType](bool success, LDAPConfigData confData) {
663 if (!success)
664 {
665 messages::internalError(asyncResp->res);
666 return;
667 }
668 parseLDAPConfigData(asyncResp->res.jsonValue, confData);
669 if (confData.serviceEnabled)
670 {
671 // Disable the service first and update the rest of
672 // the properties.
673 handleServiceEnablePatch(false, asyncResp, serverType,
674 ldapConfigObject);
675 }
676
677 if (serviceAddressList)
678 {
679 handleServiceAddressPatch(*serviceAddressList, asyncResp,
680 serverType, ldapConfigObject);
681 }
682 if (userName)
683 {
684 handleUserNamePatch(*userName, asyncResp, serverType,
685 ldapConfigObject);
686 }
687 if (password)
688 {
689 handlePasswordPatch(*password, asyncResp, serverType,
690 ldapConfigObject);
691 }
692
693 if (baseDNList)
694 {
695 handleBaseDNPatch(*baseDNList, asyncResp, serverType,
696 ldapConfigObject);
697 }
698 if (userNameAttribute)
699 {
700 handleUserNameAttrPatch(*userNameAttribute, asyncResp,
701 serverType, ldapConfigObject);
702 }
703 if (groupsAttribute)
704 {
705 handleGroupNameAttrPatch(*groupsAttribute, asyncResp,
706 serverType, ldapConfigObject);
707 }
708 if (serviceEnabled)
709 {
710 // if user has given the value as true then enable
711 // the service. if user has given false then no-op
712 // as service is already stopped.
713 if (*serviceEnabled)
714 {
715 handleServiceEnablePatch(*serviceEnabled, asyncResp,
716 serverType, ldapConfigObject);
717 }
718 }
719 else
720 {
721 // if user has not given the service enabled value
722 // then revert it to the same state as it was
723 // before.
724 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
725 serverType, ldapConfigObject);
726 }
727 });
728 }
729
Ed Tanous1abe55e2018-09-05 08:30:59 -0700730 void doGet(crow::Response& res, const crow::Request& req,
731 const std::vector<std::string>& params) override
732 {
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530733 auto asyncResp = std::make_shared<AsyncResp>(res);
734 res.jsonValue = {
735 {"@odata.context", "/redfish/v1/"
736 "$metadata#AccountService.AccountService"},
737 {"@odata.id", "/redfish/v1/AccountService"},
738 {"@odata.type", "#AccountService."
Ratan Gupta6973a582018-12-13 18:25:44 +0530739 "v1_3_1.AccountService"},
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530740 {"Id", "AccountService"},
741 {"Name", "Account Service"},
742 {"Description", "Account Service"},
743 {"ServiceEnabled", true},
AppaRao Puli343ff2e2019-03-24 00:42:13 +0530744 {"MaxPasswordLength", 20},
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530745 {"Accounts",
746 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
747 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}}};
Ed Tanous0f74e642018-11-12 15:17:05 -0800748
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530749 crow::connections::systemBus->async_method_call(
750 [asyncResp](
751 const boost::system::error_code ec,
752 const std::vector<std::pair<
Ed Tanousabf2add2019-01-22 16:40:12 -0800753 std::string, std::variant<uint32_t, uint16_t, uint8_t>>>&
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530754 propertiesList) {
755 if (ec)
756 {
757 messages::internalError(asyncResp->res);
758 return;
759 }
760 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
761 << "properties for AccountService";
762 for (const std::pair<std::string,
Ed Tanousabf2add2019-01-22 16:40:12 -0800763 std::variant<uint32_t, uint16_t, uint8_t>>&
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530764 property : propertiesList)
765 {
766 if (property.first == "MinPasswordLength")
767 {
768 const uint8_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800769 std::get_if<uint8_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530770 if (value != nullptr)
771 {
772 asyncResp->res.jsonValue["MinPasswordLength"] =
773 *value;
774 }
775 }
776 if (property.first == "AccountUnlockTimeout")
777 {
778 const uint32_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800779 std::get_if<uint32_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530780 if (value != nullptr)
781 {
782 asyncResp->res.jsonValue["AccountLockoutDuration"] =
783 *value;
784 }
785 }
786 if (property.first == "MaxLoginAttemptBeforeLockout")
787 {
788 const uint16_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800789 std::get_if<uint16_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530790 if (value != nullptr)
791 {
792 asyncResp->res
793 .jsonValue["AccountLockoutThreshold"] = *value;
794 }
795 }
796 }
797 },
798 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
799 "org.freedesktop.DBus.Properties", "GetAll",
800 "xyz.openbmc_project.User.AccountPolicy");
Ratan Gupta6973a582018-12-13 18:25:44 +0530801
802 std::string ldapType = "LDAP";
803 getLDAPConfigData(
804 ldapType,
805 [asyncResp, ldapType](bool success, LDAPConfigData& confData) {
806 parseLDAPConfigData(asyncResp->res.jsonValue, confData);
807 });
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530808 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530809
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530810 void doPatch(crow::Response& res, const crow::Request& req,
811 const std::vector<std::string>& params) override
812 {
813 auto asyncResp = std::make_shared<AsyncResp>(res);
814
815 std::optional<uint32_t> unlockTimeout;
816 std::optional<uint16_t> lockoutThreshold;
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530817 std::optional<uint16_t> minPasswordLength;
818 std::optional<uint16_t> maxPasswordLength;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530819 std::optional<nlohmann::json> ldapObject;
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530820
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530821 if (!json_util::readJson(req, res, "AccountLockoutDuration",
822 unlockTimeout, "AccountLockoutThreshold",
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530823 lockoutThreshold, "MaxPasswordLength",
824 maxPasswordLength, "MinPasswordLength",
825 minPasswordLength))
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530826 {
827 return;
828 }
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530829
830 if (minPasswordLength)
831 {
832 messages::propertyNotWritable(asyncResp->res, "MinPasswordLength");
833 }
834
835 if (maxPasswordLength)
836 {
837 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
838 }
839
Ratan Gupta8a07d282019-03-16 08:33:47 +0530840 if (ldapObject)
841 {
842 handleLDAPPatch(*ldapObject, asyncResp, req, params, "LDAP");
843 }
844
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530845 if (unlockTimeout)
846 {
847 crow::connections::systemBus->async_method_call(
848 [asyncResp](const boost::system::error_code ec) {
849 if (ec)
850 {
851 messages::internalError(asyncResp->res);
852 return;
853 }
Ratan Guptaadd61332019-02-13 20:49:16 +0530854 messages::success(asyncResp->res);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530855 },
856 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
857 "org.freedesktop.DBus.Properties", "Set",
858 "xyz.openbmc_project.User.AccountPolicy",
Ed Tanousabf2add2019-01-22 16:40:12 -0800859 "AccountUnlockTimeout", std::variant<uint32_t>(*unlockTimeout));
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530860 }
861 if (lockoutThreshold)
862 {
863 crow::connections::systemBus->async_method_call(
864 [asyncResp](const boost::system::error_code ec) {
865 if (ec)
866 {
867 messages::internalError(asyncResp->res);
868 return;
869 }
Ratan Guptaadd61332019-02-13 20:49:16 +0530870 messages::success(asyncResp->res);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530871 },
872 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
873 "org.freedesktop.DBus.Properties", "Set",
874 "xyz.openbmc_project.User.AccountPolicy",
875 "MaxLoginAttemptBeforeLockout",
Ed Tanousabf2add2019-01-22 16:40:12 -0800876 std::variant<uint16_t>(*lockoutThreshold));
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530877 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700878 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +0100879};
Tanousf00032d2018-11-05 01:18:10 -0300880
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700881class AccountsCollection : public Node
882{
883 public:
884 AccountsCollection(CrowApp& app) :
885 Node(app, "/redfish/v1/AccountService/Accounts/")
886 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700887 entityPrivileges = {
888 {boost::beast::http::verb::get,
889 {{"ConfigureUsers"}, {"ConfigureManager"}}},
890 {boost::beast::http::verb::head, {{"Login"}}},
891 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
892 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
893 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
894 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
895 }
896
897 private:
898 void doGet(crow::Response& res, const crow::Request& req,
899 const std::vector<std::string>& params) override
900 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700901 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous0f74e642018-11-12 15:17:05 -0800902 res.jsonValue = {{"@odata.context",
903 "/redfish/v1/"
904 "$metadata#ManagerAccountCollection."
905 "ManagerAccountCollection"},
906 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
907 {"@odata.type", "#ManagerAccountCollection."
908 "ManagerAccountCollection"},
909 {"Name", "Accounts Collection"},
910 {"Description", "BMC User Accounts"}};
911
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700912 crow::connections::systemBus->async_method_call(
913 [asyncResp](const boost::system::error_code ec,
914 const ManagedObjectType& users) {
915 if (ec)
916 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700917 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700918 return;
919 }
920
921 nlohmann::json& memberArray =
922 asyncResp->res.jsonValue["Members"];
923 memberArray = nlohmann::json::array();
924
925 asyncResp->res.jsonValue["Members@odata.count"] = users.size();
926 for (auto& user : users)
927 {
928 const std::string& path =
929 static_cast<const std::string&>(user.first);
930 std::size_t lastIndex = path.rfind("/");
931 if (lastIndex == std::string::npos)
932 {
933 lastIndex = 0;
934 }
935 else
936 {
937 lastIndex += 1;
938 }
939 memberArray.push_back(
940 {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
941 path.substr(lastIndex)}});
942 }
943 },
944 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
945 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
946 }
Ed Tanous04ae99e2018-09-20 15:54:36 -0700947 void doPost(crow::Response& res, const crow::Request& req,
948 const std::vector<std::string>& params) override
949 {
950 auto asyncResp = std::make_shared<AsyncResp>(res);
951
Ed Tanous9712f8a2018-09-21 13:38:49 -0700952 std::string username;
953 std::string password;
Ed Tanousa24526d2018-12-10 15:17:59 -0800954 std::optional<std::string> roleId("User");
955 std::optional<bool> enabled = true;
Ed Tanous9712f8a2018-09-21 13:38:49 -0700956 if (!json_util::readJson(req, res, "UserName", username, "Password",
957 password, "RoleId", roleId, "Enabled",
958 enabled))
Ed Tanous04ae99e2018-09-20 15:54:36 -0700959 {
960 return;
961 }
962
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530963 std::string priv = getRoleIdFromPrivilege(*roleId);
964 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -0700965 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700966 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
Ed Tanous04ae99e2018-09-20 15:54:36 -0700967 return;
968 }
Ed Tanous9712f8a2018-09-21 13:38:49 -0700969 roleId = priv;
Ed Tanous04ae99e2018-09-20 15:54:36 -0700970
971 crow::connections::systemBus->async_method_call(
Ed Tanous9712f8a2018-09-21 13:38:49 -0700972 [asyncResp, username, password{std::move(password)}](
Ed Tanous04ae99e2018-09-20 15:54:36 -0700973 const boost::system::error_code ec) {
974 if (ec)
975 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700976 messages::resourceAlreadyExists(
977 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
978 "UserName", username);
Ed Tanous04ae99e2018-09-20 15:54:36 -0700979 return;
980 }
981
982 if (!pamUpdatePassword(username, password))
983 {
984 // At this point we have a user that's been created, but the
985 // password set failed. Something is wrong, so delete the
986 // user that we've already created
987 crow::connections::systemBus->async_method_call(
988 [asyncResp](const boost::system::error_code ec) {
989 if (ec)
990 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700991 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -0700992 return;
993 }
994
Jason M. Billsf12894f2018-10-09 12:45:45 -0700995 messages::invalidObject(asyncResp->res, "Password");
Ed Tanous04ae99e2018-09-20 15:54:36 -0700996 },
997 "xyz.openbmc_project.User.Manager",
998 "/xyz/openbmc_project/user/" + username,
999 "xyz.openbmc_project.Object.Delete", "Delete");
1000
1001 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1002 return;
1003 }
1004
Jason M. Billsf12894f2018-10-09 12:45:45 -07001005 messages::created(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001006 asyncResp->res.addHeader(
1007 "Location",
1008 "/redfish/v1/AccountService/Accounts/" + username);
1009 },
1010 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ed Tanous9712f8a2018-09-21 13:38:49 -07001011 "xyz.openbmc_project.User.Manager", "CreateUser", username,
Ed Tanous04ae99e2018-09-20 15:54:36 -07001012 std::array<const char*, 4>{"ipmi", "redfish", "ssh", "web"},
Ed Tanous9712f8a2018-09-21 13:38:49 -07001013 *roleId, *enabled);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001014 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001015};
1016
1017class ManagerAccount : public Node
1018{
1019 public:
1020 ManagerAccount(CrowApp& app) :
1021 Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
1022 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001023 entityPrivileges = {
1024 {boost::beast::http::verb::get,
1025 {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
1026 {boost::beast::http::verb::head, {{"Login"}}},
1027 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
1028 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
1029 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
1030 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
1031 }
1032
1033 private:
1034 void doGet(crow::Response& res, const crow::Request& req,
1035 const std::vector<std::string>& params) override
1036 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001037 res.jsonValue = {
1038 {"@odata.context",
1039 "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
1040 {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
Ed Tanous0f74e642018-11-12 15:17:05 -08001041 {"Name", "User Account"},
1042 {"Description", "User Account"},
1043 {"Password", nullptr},
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301044 {"RoleId", "Administrator"}};
Ed Tanous0f74e642018-11-12 15:17:05 -08001045
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001046 auto asyncResp = std::make_shared<AsyncResp>(res);
1047
1048 if (params.size() != 1)
1049 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001050 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001051 return;
1052 }
1053
1054 crow::connections::systemBus->async_method_call(
1055 [asyncResp, accountName{std::string(params[0])}](
1056 const boost::system::error_code ec,
1057 const ManagedObjectType& users) {
1058 if (ec)
1059 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001060 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001061 return;
1062 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301063 auto userIt = users.begin();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001064
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301065 for (; userIt != users.end(); userIt++)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001066 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301067 if (boost::ends_with(userIt->first.str, "/" + accountName))
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001068 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301069 break;
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001070 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301071 }
1072 if (userIt == users.end())
1073 {
1074 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1075 accountName);
1076 return;
1077 }
1078 for (const auto& interface : userIt->second)
1079 {
1080 if (interface.first ==
1081 "xyz.openbmc_project.User.Attributes")
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001082 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301083 for (const auto& property : interface.second)
Ed Tanous65b0dc32018-09-19 16:04:03 -07001084 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301085 if (property.first == "UserEnabled")
Ed Tanous65b0dc32018-09-19 16:04:03 -07001086 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301087 const bool* userEnabled =
Ed Tanousabf2add2019-01-22 16:40:12 -08001088 std::get_if<bool>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301089 if (userEnabled == nullptr)
Ed Tanous65b0dc32018-09-19 16:04:03 -07001090 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301091 BMCWEB_LOG_ERROR
1092 << "UserEnabled wasn't a bool";
1093 messages::internalError(asyncResp->res);
1094 return;
Ed Tanous65b0dc32018-09-19 16:04:03 -07001095 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301096 asyncResp->res.jsonValue["Enabled"] =
1097 *userEnabled;
1098 }
1099 else if (property.first ==
1100 "UserLockedForFailedAttempt")
1101 {
1102 const bool* userLocked =
Ed Tanousabf2add2019-01-22 16:40:12 -08001103 std::get_if<bool>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301104 if (userLocked == nullptr)
1105 {
1106 BMCWEB_LOG_ERROR << "UserLockedForF"
1107 "ailedAttempt "
1108 "wasn't a bool";
1109 messages::internalError(asyncResp->res);
1110 return;
1111 }
1112 asyncResp->res.jsonValue["Locked"] =
1113 *userLocked;
Ratan Gupta24c85422019-01-30 19:41:24 +05301114 asyncResp->res.jsonValue
1115 ["Locked@Redfish.AllowableValues"] = {
Gunnar Mills4d64ce32019-03-29 16:34:56 -05001116 "false"};
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301117 }
1118 else if (property.first == "UserPrivilege")
1119 {
1120 const std::string* userRolePtr =
Ed Tanousabf2add2019-01-22 16:40:12 -08001121 std::get_if<std::string>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301122 if (userRolePtr == nullptr)
1123 {
1124 BMCWEB_LOG_ERROR
1125 << "UserPrivilege wasn't a "
1126 "string";
1127 messages::internalError(asyncResp->res);
1128 return;
1129 }
1130 std::string priv =
1131 getPrivilegeFromRoleId(*userRolePtr);
1132 if (priv.empty())
1133 {
1134 BMCWEB_LOG_ERROR << "Invalid user role";
1135 messages::internalError(asyncResp->res);
1136 return;
1137 }
1138 asyncResp->res.jsonValue["RoleId"] = priv;
1139
1140 asyncResp->res.jsonValue["Links"]["Role"] = {
1141 {"@odata.id", "/redfish/v1/AccountService/"
1142 "Roles/" +
1143 priv}};
Ed Tanous65b0dc32018-09-19 16:04:03 -07001144 }
1145 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001146 }
1147 }
1148
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301149 asyncResp->res.jsonValue["@odata.id"] =
1150 "/redfish/v1/AccountService/Accounts/" + accountName;
1151 asyncResp->res.jsonValue["Id"] = accountName;
1152 asyncResp->res.jsonValue["UserName"] = accountName;
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001153 },
1154 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1155 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1156 }
Ed Tanousa8408792018-09-05 16:08:38 -07001157
1158 void doPatch(crow::Response& res, const crow::Request& req,
1159 const std::vector<std::string>& params) override
1160 {
1161 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousa8408792018-09-05 16:08:38 -07001162 if (params.size() != 1)
1163 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001164 messages::internalError(asyncResp->res);
Ed Tanousa8408792018-09-05 16:08:38 -07001165 return;
1166 }
1167
Ed Tanousa24526d2018-12-10 15:17:59 -08001168 std::optional<std::string> newUserName;
1169 std::optional<std::string> password;
1170 std::optional<bool> enabled;
1171 std::optional<std::string> roleId;
Ratan Gupta24c85422019-01-30 19:41:24 +05301172 std::optional<bool> locked;
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301173 if (!json_util::readJson(req, res, "UserName", newUserName, "Password",
Ratan Gupta24c85422019-01-30 19:41:24 +05301174 password, "RoleId", roleId, "Enabled", enabled,
1175 "Locked", locked))
Ed Tanousa8408792018-09-05 16:08:38 -07001176 {
1177 return;
1178 }
1179
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301180 const std::string& username = params[0];
Ed Tanousa8408792018-09-05 16:08:38 -07001181
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301182 if (!newUserName)
1183 {
1184 // If the username isn't being updated, we can update the properties
1185 // directly
Ratan Gupta24c85422019-01-30 19:41:24 +05301186 updateUserProperties(asyncResp, username, password, enabled, roleId,
1187 locked);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301188 return;
1189 }
1190 else
1191 {
1192 crow::connections::systemBus->async_method_call(
1193 [this, asyncResp, username, password(std::move(password)),
1194 roleId(std::move(roleId)), enabled(std::move(enabled)),
Ratan Gupta24c85422019-01-30 19:41:24 +05301195 newUser{std::string(*newUserName)}, locked(std::move(locked))](
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301196 const boost::system::error_code ec) {
1197 if (ec)
Ed Tanousa8408792018-09-05 16:08:38 -07001198 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301199 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1200 messages::resourceNotFound(
1201 asyncResp->res,
1202 "#ManagerAccount.v1_0_3.ManagerAccount", username);
1203 return;
1204 }
1205
1206 updateUserProperties(asyncResp, newUser, password, enabled,
Ratan Gupta24c85422019-01-30 19:41:24 +05301207 roleId, locked);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301208 },
1209 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1210 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1211 *newUserName);
1212 }
1213 }
1214
1215 void updateUserProperties(std::shared_ptr<AsyncResp> asyncResp,
1216 const std::string& username,
Ed Tanousa24526d2018-12-10 15:17:59 -08001217 std::optional<std::string> password,
1218 std::optional<bool> enabled,
Ratan Gupta24c85422019-01-30 19:41:24 +05301219 std::optional<std::string> roleId,
1220 std::optional<bool> locked)
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301221 {
1222 if (password)
1223 {
1224 if (!pamUpdatePassword(username, *password))
1225 {
1226 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1227 messages::internalError(asyncResp->res);
1228 return;
1229 }
1230 }
1231
Ratan Gupta24c85422019-01-30 19:41:24 +05301232 std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
1233 dbus::utility::escapePathForDbus(dbusObjectPath);
1234
Ratan Gupta22c33712019-05-03 21:50:28 +05301235 dbus::utility::checkDbusPathExists(
Ratan Gupta24c85422019-01-30 19:41:24 +05301236 dbusObjectPath,
1237 [dbusObjectPath(std::move(dbusObjectPath)), username,
1238 password(std::move(password)), roleId(std::move(roleId)),
1239 enabled(std::move(enabled)), locked(std::move(locked)),
1240 asyncResp{std::move(asyncResp)}](int rc) {
1241 if (!rc)
1242 {
1243 messages::invalidObject(asyncResp->res, username.c_str());
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301244 return;
Ratan Gupta24c85422019-01-30 19:41:24 +05301245 }
1246 if (enabled)
1247 {
1248 crow::connections::systemBus->async_method_call(
1249 [asyncResp](const boost::system::error_code ec) {
1250 if (ec)
1251 {
1252 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1253 << ec;
1254 messages::internalError(asyncResp->res);
1255 return;
1256 }
1257 messages::success(asyncResp->res);
1258 return;
1259 },
1260 "xyz.openbmc_project.User.Manager",
1261 dbusObjectPath.c_str(),
1262 "org.freedesktop.DBus.Properties", "Set",
1263 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1264 std::variant<bool>{*enabled});
1265 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001266
Ratan Gupta24c85422019-01-30 19:41:24 +05301267 if (roleId)
1268 {
1269 std::string priv = getRoleIdFromPrivilege(*roleId);
1270 if (priv.empty())
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301271 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301272 messages::propertyValueNotInList(asyncResp->res,
1273 *roleId, "RoleId");
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301274 return;
1275 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301276
1277 crow::connections::systemBus->async_method_call(
1278 [asyncResp](const boost::system::error_code ec) {
1279 if (ec)
1280 {
1281 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1282 << ec;
1283 messages::internalError(asyncResp->res);
1284 return;
1285 }
1286 messages::success(asyncResp->res);
1287 },
1288 "xyz.openbmc_project.User.Manager",
1289 dbusObjectPath.c_str(),
1290 "org.freedesktop.DBus.Properties", "Set",
1291 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1292 std::variant<std::string>{priv});
1293 }
1294
1295 if (locked)
1296 {
1297 // admin can unlock the account which is locked by
1298 // successive authentication failures but admin should not
1299 // be allowed to lock an account.
1300 if (*locked)
1301 {
1302 messages::propertyValueNotInList(asyncResp->res, "true",
1303 "Locked");
1304 return;
1305 }
1306
1307 crow::connections::systemBus->async_method_call(
1308 [asyncResp](const boost::system::error_code ec) {
1309 if (ec)
1310 {
1311 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1312 << ec;
1313 messages::internalError(asyncResp->res);
1314 return;
1315 }
1316 messages::success(asyncResp->res);
1317 return;
1318 },
1319 "xyz.openbmc_project.User.Manager",
1320 dbusObjectPath.c_str(),
1321 "org.freedesktop.DBus.Properties", "Set",
1322 "xyz.openbmc_project.User.Attributes",
1323 "UserLockedForFailedAttempt",
1324 sdbusplus::message::variant<bool>{*locked});
1325 }
1326 });
Ed Tanousa8408792018-09-05 16:08:38 -07001327 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001328
1329 void doDelete(crow::Response& res, const crow::Request& req,
1330 const std::vector<std::string>& params) override
1331 {
1332 auto asyncResp = std::make_shared<AsyncResp>(res);
1333
1334 if (params.size() != 1)
1335 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001336 messages::internalError(asyncResp->res);
Ed Tanous06e086d2018-09-19 17:19:52 -07001337 return;
1338 }
1339
1340 const std::string userPath = "/xyz/openbmc_project/user/" + params[0];
1341
1342 crow::connections::systemBus->async_method_call(
1343 [asyncResp, username{std::move(params[0])}](
1344 const boost::system::error_code ec) {
1345 if (ec)
1346 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001347 messages::resourceNotFound(
1348 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
1349 username);
Ed Tanous06e086d2018-09-19 17:19:52 -07001350 return;
1351 }
1352
Jason M. Billsf12894f2018-10-09 12:45:45 -07001353 messages::accountRemoved(asyncResp->res);
Ed Tanous06e086d2018-09-19 17:19:52 -07001354 },
1355 "xyz.openbmc_project.User.Manager", userPath,
1356 "xyz.openbmc_project.Object.Delete", "Delete");
1357 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301358};
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001359
Ed Tanous1abe55e2018-09-05 08:30:59 -07001360} // namespace redfish