blob: b4f6f96e6a554358a4b4d437bf179b20f5ab1351 [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";
40constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
41constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
42constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
43constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
44constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
45
46struct LDAPConfigData
47{
48 std::string uri{};
49 std::string bindDN{};
50 std::string baseDN{};
51 std::string searchScope{};
52 std::string serverType{};
53 bool serviceEnabled = false;
54 std::string userNameAttribute{};
55 std::string groupAttribute{};
56};
57
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070058using ManagedObjectType = std::vector<std::pair<
59 sdbusplus::message::object_path,
60 boost::container::flat_map<
Ed Tanousabf2add2019-01-22 16:40:12 -080061 std::string, boost::container::flat_map<
62 std::string, std::variant<bool, std::string>>>>>;
Ratan Gupta6973a582018-12-13 18:25:44 +053063using GetObjectType =
64 std::vector<std::pair<std::string, std::vector<std::string>>>;
AppaRao Puli84e12cb2018-10-11 01:28:15 +053065
Adriana Kobylakae29b8c2019-04-24 11:19:18 -050066inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053067{
68 if (role == "priv-admin")
69 {
70 return "Administrator";
71 }
72 else if (role == "priv-callback")
73 {
74 return "Callback";
75 }
76 else if (role == "priv-user")
77 {
78 return "User";
79 }
80 else if (role == "priv-operator")
81 {
82 return "Operator";
83 }
84 return "";
85}
Adriana Kobylakae29b8c2019-04-24 11:19:18 -050086inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053087{
88 if (role == "Administrator")
89 {
90 return "priv-admin";
91 }
92 else if (role == "Callback")
93 {
94 return "priv-callback";
95 }
96 else if (role == "User")
97 {
98 return "priv-user";
99 }
100 else if (role == "Operator")
101 {
102 return "priv-operator";
103 }
104 return "";
105}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700106
Ratan Gupta6973a582018-12-13 18:25:44 +0530107void parseLDAPConfigData(nlohmann::json& json_response,
Ratan Guptaab828d72019-04-22 14:18:33 +0530108 const LDAPConfigData& confData,
109 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530110{
Ratan Guptaab828d72019-04-22 14:18:33 +0530111 std::string service =
112 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
113 json_response[ldapType] = {
Ratan Gupta6973a582018-12-13 18:25:44 +0530114 {"AccountProviderType", service},
Ratan Gupta6973a582018-12-13 18:25:44 +0530115 {"ServiceEnabled", confData.serviceEnabled},
116 {"ServiceAddresses", nlohmann::json::array({confData.uri})},
117 {"Authentication",
118 {{"AuthenticationType", "UsernameAndPassword"},
Ratan Gupta6973a582018-12-13 18:25:44 +0530119 {"Username", confData.bindDN},
120 {"Password", nullptr}}},
121 {"LDAPService",
122 {{"SearchSettings",
123 {{"BaseDistinguishedNames",
124 nlohmann::json::array({confData.baseDN})},
125 {"UsernameAttribute", confData.userNameAttribute},
126 {"GroupsAttribute", confData.groupAttribute}}}}},
127 };
128}
129
130/**
131 * Function that retrieves all properties for LDAP config object
132 * into JSON
133 */
134template <typename CallbackFunc>
135inline void getLDAPConfigData(const std::string& ldapType,
136 CallbackFunc&& callback)
137{
138 auto getConfig = [callback,
139 ldapType](const boost::system::error_code error_code,
140 const ManagedObjectType& ldapObjects) {
141 LDAPConfigData confData{};
142 if (error_code)
143 {
Ratan Guptaab828d72019-04-22 14:18:33 +0530144 callback(false, confData, ldapType);
Ratan Gupta6973a582018-12-13 18:25:44 +0530145 BMCWEB_LOG_ERROR << "D-Bus responses error: " << error_code;
146 return;
147 }
Ratan Guptaab828d72019-04-22 14:18:33 +0530148
149 std::string ldapDbusType;
150 if (ldapType == "LDAP")
151 {
152 ldapDbusType = "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
153 }
154 else if (ldapType == "ActiveDirectory")
155 {
156 ldapDbusType = "xyz.openbmc_project.User.Ldap.Config.Type."
157 "ActiveDirectory";
158 }
159 else
160 {
161 BMCWEB_LOG_ERROR << "Can't get the DbusType for the given type="
162 << ldapType;
163 callback(false, confData, ldapType);
164 return;
165 }
166
167 std::string ldapEnableInterfaceStr = ldapEnableInterface;
168 std::string ldapConfigInterfaceStr = ldapConfigInterface;
169
Ratan Gupta6973a582018-12-13 18:25:44 +0530170 for (const auto& object : ldapObjects)
171 {
Ratan Guptaab828d72019-04-22 14:18:33 +0530172 // let's find the object whose ldap type is equal to the given type
173 auto intfit = object.second.find(ldapConfigInterfaceStr);
174 if (intfit == object.second.end())
Ratan Gupta6973a582018-12-13 18:25:44 +0530175 {
Ratan Guptaab828d72019-04-22 14:18:33 +0530176 continue;
177 }
178 auto propit = intfit->second.find("LDAPType");
179 if (propit == intfit->second.end())
180 {
181 continue;
182 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530183
Ratan Guptaab828d72019-04-22 14:18:33 +0530184 const std::string* value =
185 std::get_if<std::string>(&(propit->second));
186 if (value == nullptr || (*value) != ldapDbusType)
187 {
188
189 // this is not the interested configuration,
190 // let's move on to the other configuration.
191 continue;
192 }
193 else
194 {
195 confData.serverType = *value;
196 }
197
198 for (const auto& interface : object.second)
199 {
200 if (interface.first == ldapEnableInterfaceStr)
201 {
202 // rest of the properties are string.
203 for (const auto& property : interface.second)
204 {
205 if (property.first == "Enabled")
Ratan Gupta6973a582018-12-13 18:25:44 +0530206 {
Ratan Guptaab828d72019-04-22 14:18:33 +0530207 const bool* value =
208 std::get_if<bool>(&property.second);
Ratan Gupta6973a582018-12-13 18:25:44 +0530209 if (value == nullptr)
210 {
211 continue;
212 }
Ratan Guptaab828d72019-04-22 14:18:33 +0530213 confData.serviceEnabled = *value;
214 break;
Ratan Gupta6973a582018-12-13 18:25:44 +0530215 }
216 }
217 }
Ratan Guptaab828d72019-04-22 14:18:33 +0530218 else if (interface.first == ldapConfigInterfaceStr)
219 {
Ratan Gupta6973a582018-12-13 18:25:44 +0530220
Ratan Guptaab828d72019-04-22 14:18:33 +0530221 for (const auto& property : interface.second)
222 {
223 const std::string* value =
224 std::get_if<std::string>(&property.second);
225 if (value == nullptr)
226 {
227 continue;
228 }
229 if (property.first == "LDAPServerURI")
230 {
231 confData.uri = *value;
232 }
233 else if (property.first == "LDAPBindDN")
234 {
235 confData.bindDN = *value;
236 }
237 else if (property.first == "LDAPBaseDN")
238 {
239 confData.baseDN = *value;
240 }
241 else if (property.first == "LDAPSearchScope")
242 {
243 confData.searchScope = *value;
244 }
245 else if (property.first == "GroupNameAttribute")
246 {
247 confData.groupAttribute = *value;
248 }
249 else if (property.first == "UserNameAttribute")
250 {
251 confData.userNameAttribute = *value;
252 }
253 }
254 }
255 }
256 if (confData.serverType == ldapDbusType)
257 {
258 callback(true, confData, ldapType);
Ratan Gupta6973a582018-12-13 18:25:44 +0530259 break;
260 }
261 }
262 };
Ratan Guptaab828d72019-04-22 14:18:33 +0530263 auto getServiceName = [callback, ldapType, getConfig(std::move(getConfig))](
Ratan Gupta6973a582018-12-13 18:25:44 +0530264 const boost::system::error_code ec,
265 const GetObjectType& resp) {
266 LDAPConfigData confData{};
267 if (ec || resp.empty())
268 {
269 BMCWEB_LOG_ERROR
270 << "DBUS response error during getting of service name: " << ec;
Ratan Guptaab828d72019-04-22 14:18:33 +0530271 callback(false, confData, ldapType);
Ratan Gupta6973a582018-12-13 18:25:44 +0530272 return;
273 }
274 std::string service = resp.begin()->first;
275 crow::connections::systemBus->async_method_call(
276 std::move(getConfig), service, ldapRootObject, dbusObjManagerIntf,
277 "GetManagedObjects");
278 };
279
280 const std::array<std::string, 2> interfaces = {ldapEnableInterface,
281 ldapConfigInterface};
282
283 crow::connections::systemBus->async_method_call(
284 std::move(getServiceName), mapperBusName, mapperObjectPath, mapperIntf,
285 "GetObject", ldapConfigObject, interfaces);
286}
287
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288class AccountService : public Node
289{
290 public:
291 AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/")
292 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700293 entityPrivileges = {
294 {boost::beast::http::verb::get,
295 {{"ConfigureUsers"}, {"ConfigureManager"}}},
296 {boost::beast::http::verb::head, {{"Login"}}},
297 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
298 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
299 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
300 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
301 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +0100302
Ed Tanous1abe55e2018-09-05 08:30:59 -0700303 private:
Ratan Gupta8a07d282019-03-16 08:33:47 +0530304 /**
305 * @brief parses the authentication section under the LDAP
306 * @param input JSON data
307 * @param asyncResp pointer to the JSON response
308 * @param userName userName to be filled from the given JSON.
309 * @param password password to be filled from the given JSON.
310 */
311 void
312 parseLDAPAuthenticationJson(nlohmann::json input,
313 const std::shared_ptr<AsyncResp>& asyncResp,
314 std::optional<std::string>& username,
315 std::optional<std::string>& password)
316 {
317 std::optional<std::string> authType;
318
319 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
320 authType, "Username", username, "Password",
321 password))
322 {
323 return;
324 }
325 if (!authType)
326 {
327 return;
328 }
329 if (*authType != "UsernameAndPassword")
330 {
331 messages::propertyValueNotInList(asyncResp->res, *authType,
332 "AuthenticationType");
333 return;
334 }
335 }
336 /**
337 * @brief parses the LDAPService section under the LDAP
338 * @param input JSON data
339 * @param asyncResp pointer to the JSON response
340 * @param baseDNList baseDN to be filled from the given JSON.
341 * @param userNameAttribute userName to be filled from the given JSON.
342 * @param groupaAttribute password to be filled from the given JSON.
343 */
344
345 void parseLDAPServiceJson(
346 nlohmann::json input, const std::shared_ptr<AsyncResp>& asyncResp,
347 std::optional<std::vector<std::string>>& baseDNList,
348 std::optional<std::string>& userNameAttribute,
349 std::optional<std::string>& groupsAttribute)
350 {
351 std::optional<nlohmann::json> searchSettings;
352
353 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
354 searchSettings))
355 {
356 return;
357 }
358 if (!searchSettings)
359 {
360 return;
361 }
362 if (!json_util::readJson(*searchSettings, asyncResp->res,
363 "BaseDistinguishedNames", baseDNList,
364 "UsernameAttribute", userNameAttribute,
365 "GroupsAttribute", groupsAttribute))
366 {
367 return;
368 }
369 }
370 /**
371 * @brief updates the LDAP server address and updates the
372 json response with the new value.
373 * @param serviceAddressList address to be updated.
374 * @param asyncResp pointer to the JSON response
375 * @param ldapServerElementName Type of LDAP
376 server(openLDAP/ActiveDirectory)
377 */
378
379 void handleServiceAddressPatch(
380 const std::vector<std::string>& serviceAddressList,
381 const std::shared_ptr<AsyncResp>& asyncResp,
382 const std::string& ldapServerElementName,
383 const std::string& ldapConfigObject)
384 {
385 crow::connections::systemBus->async_method_call(
386 [asyncResp, ldapServerElementName,
387 serviceAddressList](const boost::system::error_code ec) {
388 if (ec)
389 {
390 BMCWEB_LOG_DEBUG
391 << "Error Occured in updating the service address";
392 messages::internalError(asyncResp->res);
393 return;
394 }
395 std::vector<std::string> modifiedserviceAddressList = {
396 serviceAddressList.front()};
397 asyncResp->res
398 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
399 modifiedserviceAddressList;
400 if ((serviceAddressList).size() > 1)
401 {
402 messages::propertyValueModified(asyncResp->res,
403 "ServiceAddresses",
404 serviceAddressList.front());
405 }
406 BMCWEB_LOG_DEBUG << "Updated the service address";
407 },
408 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
409 ldapConfigInterface, "LDAPServerURI",
410 std::variant<std::string>(serviceAddressList.front()));
411 }
412 /**
413 * @brief updates the LDAP Bind DN and updates the
414 json response with the new value.
415 * @param username name of the user which needs to be updated.
416 * @param asyncResp pointer to the JSON response
417 * @param ldapServerElementName Type of LDAP
418 server(openLDAP/ActiveDirectory)
419 */
420
421 void handleUserNamePatch(const std::string& username,
422 const std::shared_ptr<AsyncResp>& asyncResp,
423 const std::string& ldapServerElementName,
424 const std::string& ldapConfigObject)
425 {
426 crow::connections::systemBus->async_method_call(
427 [asyncResp, username,
428 ldapServerElementName](const boost::system::error_code ec) {
429 if (ec)
430 {
431 BMCWEB_LOG_DEBUG
432 << "Error occured in updating the username";
433 messages::internalError(asyncResp->res);
434 return;
435 }
436 asyncResp->res.jsonValue[ldapServerElementName]
437 ["Authentication"]["Username"] =
438 username;
439 BMCWEB_LOG_DEBUG << "Updated the username";
440 },
441 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
442 ldapConfigInterface, "LDAPBindDN",
443 std::variant<std::string>(username));
444 }
445
446 /**
447 * @brief updates the LDAP password
448 * @param password : ldap password which needs to be updated.
449 * @param asyncResp pointer to the JSON response
450 * @param ldapServerElementName Type of LDAP
451 * server(openLDAP/ActiveDirectory)
452 */
453
454 void handlePasswordPatch(const std::string& password,
455 const std::shared_ptr<AsyncResp>& asyncResp,
456 const std::string& ldapServerElementName,
457 const std::string& ldapConfigObject)
458 {
459 crow::connections::systemBus->async_method_call(
460 [asyncResp, password,
461 ldapServerElementName](const boost::system::error_code ec) {
462 if (ec)
463 {
464 BMCWEB_LOG_DEBUG
465 << "Error occured in updating the password";
466 messages::internalError(asyncResp->res);
467 return;
468 }
469 asyncResp->res.jsonValue[ldapServerElementName]
470 ["Authentication"]["Password"] = "";
471 BMCWEB_LOG_DEBUG << "Updated the password";
472 },
473 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
474 ldapConfigInterface, "LDAPBindDNPassword",
475 std::variant<std::string>(password));
476 }
477
478 /**
479 * @brief updates the LDAP BaseDN and updates the
480 json response with the new value.
481 * @param baseDNList baseDN list which needs to be updated.
482 * @param asyncResp pointer to the JSON response
483 * @param ldapServerElementName Type of LDAP
484 server(openLDAP/ActiveDirectory)
485 */
486
487 void handleBaseDNPatch(const std::vector<std::string>& baseDNList,
488 const std::shared_ptr<AsyncResp>& asyncResp,
489 const std::string& ldapServerElementName,
490 const std::string& ldapConfigObject)
491 {
492 crow::connections::systemBus->async_method_call(
493 [asyncResp, baseDNList,
494 ldapServerElementName](const boost::system::error_code ec) {
495 if (ec)
496 {
497 BMCWEB_LOG_DEBUG << "Error Occured in Updating the base DN";
498 messages::internalError(asyncResp->res);
499 return;
500 }
501 auto& serverTypeJson =
502 asyncResp->res.jsonValue[ldapServerElementName];
503 auto& searchSettingsJson =
504 serverTypeJson["LDAPService"]["SearchSettings"];
505 std::vector<std::string> modifiedBaseDNList = {
506 baseDNList.front()};
507 searchSettingsJson["BaseDistinguishedNames"] =
508 modifiedBaseDNList;
509 if (baseDNList.size() > 1)
510 {
511 messages::propertyValueModified(asyncResp->res,
512 "BaseDistinguishedNames",
513 baseDNList.front());
514 }
515 BMCWEB_LOG_DEBUG << "Updated the base DN";
516 },
517 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
518 ldapConfigInterface, "LDAPBaseDN",
519 std::variant<std::string>(baseDNList.front()));
520 }
521 /**
522 * @brief updates the LDAP user name attribute and updates the
523 json response with the new value.
524 * @param userNameAttribute attribute to be updated.
525 * @param asyncResp pointer to the JSON response
526 * @param ldapServerElementName Type of LDAP
527 server(openLDAP/ActiveDirectory)
528 */
529
530 void handleUserNameAttrPatch(const std::string& userNameAttribute,
531 const std::shared_ptr<AsyncResp>& asyncResp,
532 const std::string& ldapServerElementName,
533 const std::string& ldapConfigObject)
534 {
535 crow::connections::systemBus->async_method_call(
536 [asyncResp, userNameAttribute,
537 ldapServerElementName](const boost::system::error_code ec) {
538 if (ec)
539 {
540 BMCWEB_LOG_DEBUG << "Error Occured in Updating the "
541 "username attribute";
542 messages::internalError(asyncResp->res);
543 return;
544 }
545 auto& serverTypeJson =
546 asyncResp->res.jsonValue[ldapServerElementName];
547 auto& searchSettingsJson =
548 serverTypeJson["LDAPService"]["SearchSettings"];
549 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
550 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
551 },
552 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
553 ldapConfigInterface, "UserNameAttribute",
554 std::variant<std::string>(userNameAttribute));
555 }
556 /**
557 * @brief updates the LDAP group attribute and updates the
558 json response with the new value.
559 * @param groupsAttribute attribute to be updated.
560 * @param asyncResp pointer to the JSON response
561 * @param ldapServerElementName Type of LDAP
562 server(openLDAP/ActiveDirectory)
563 */
564
565 void handleGroupNameAttrPatch(const std::string& groupsAttribute,
566 const std::shared_ptr<AsyncResp>& asyncResp,
567 const std::string& ldapServerElementName,
568 const std::string& ldapConfigObject)
569 {
570 crow::connections::systemBus->async_method_call(
571 [asyncResp, groupsAttribute,
572 ldapServerElementName](const boost::system::error_code ec) {
573 if (ec)
574 {
575 BMCWEB_LOG_DEBUG << "Error Occured in Updating the "
576 "groupname attribute";
577 messages::internalError(asyncResp->res);
578 return;
579 }
580 auto& serverTypeJson =
581 asyncResp->res.jsonValue[ldapServerElementName];
582 auto& searchSettingsJson =
583 serverTypeJson["LDAPService"]["SearchSettings"];
584 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
585 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
586 },
587 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
588 ldapConfigInterface, "GroupNameAttribute",
589 std::variant<std::string>(groupsAttribute));
590 }
591 /**
592 * @brief updates the LDAP service enable and updates the
593 json response with the new value.
594 * @param input JSON data.
595 * @param asyncResp pointer to the JSON response
596 * @param ldapServerElementName Type of LDAP
597 server(openLDAP/ActiveDirectory)
598 */
599
600 void handleServiceEnablePatch(bool serviceEnabled,
601 const std::shared_ptr<AsyncResp>& asyncResp,
602 const std::string& ldapServerElementName,
603 const std::string& ldapConfigObject)
604 {
605 crow::connections::systemBus->async_method_call(
606 [asyncResp, serviceEnabled,
607 ldapServerElementName](const boost::system::error_code ec) {
608 if (ec)
609 {
610 BMCWEB_LOG_DEBUG
611 << "Error Occured in Updating the service enable";
612 messages::internalError(asyncResp->res);
613 return;
614 }
615 asyncResp->res
616 .jsonValue[ldapServerElementName]["ServiceEnabled"] =
617 serviceEnabled;
618 BMCWEB_LOG_DEBUG << "Updated Service enable = "
619 << serviceEnabled;
620 },
621 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
622 ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
623 }
624
625 /**
626 * @brief Get the required values from the given JSON, validates the
627 * value and create the LDAP config object.
628 * @param input JSON data
629 * @param asyncResp pointer to the JSON response
630 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
631 */
632
633 void handleLDAPPatch(nlohmann::json& input,
634 const std::shared_ptr<AsyncResp>& asyncResp,
635 const crow::Request& req,
636 const std::vector<std::string>& params,
637 const std::string& serverType)
638 {
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530639 std::string dbusObjectPath;
640 if (serverType == "ActiveDirectory")
641 {
642 dbusObjectPath = ADConfigObject;
643 }
644 else if (serverType == "LDAP")
645 {
646 dbusObjectPath = ldapConfigObject;
647 }
648
Ratan Gupta8a07d282019-03-16 08:33:47 +0530649 std::optional<nlohmann::json> authentication;
650 std::optional<nlohmann::json> ldapService;
651 std::optional<std::string> accountProviderType;
652 std::optional<std::vector<std::string>> serviceAddressList;
653 std::optional<bool> serviceEnabled;
654 std::optional<std::vector<std::string>> baseDNList;
655 std::optional<std::string> userNameAttribute;
656 std::optional<std::string> groupsAttribute;
657 std::optional<std::string> userName;
658 std::optional<std::string> password;
659
660 if (!json_util::readJson(input, asyncResp->res, "Authentication",
661 authentication, "LDAPService", ldapService,
662 "ServiceAddresses", serviceAddressList,
663 "AccountProviderType", accountProviderType,
664 "ServiceEnabled", serviceEnabled))
665 {
666 return;
667 }
668
669 if (authentication)
670 {
671 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
672 password);
673 }
674 if (ldapService)
675 {
676 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
677 userNameAttribute, groupsAttribute);
678 }
679 if (accountProviderType)
680 {
681 messages::propertyNotWritable(asyncResp->res,
682 "AccountProviderType");
683 }
684 if (serviceAddressList)
685 {
686 if ((*serviceAddressList).size() == 0)
687 {
688 messages::propertyValueNotInList(asyncResp->res, "[]",
689 "ServiceAddress");
690 return;
691 }
692 }
693 if (baseDNList)
694 {
695 if ((*baseDNList).size() == 0)
696 {
697 messages::propertyValueNotInList(asyncResp->res, "[]",
698 "BaseDistinguishedNames");
699 return;
700 }
701 }
702
703 // nothing to update, then return
704 if (!userName && !password && !serviceAddressList && !baseDNList &&
705 !userNameAttribute && !groupsAttribute && !serviceEnabled)
706 {
707 return;
708 }
709
710 // Get the existing resource first then keep modifying
711 // whenever any property gets updated.
Ratan Guptaab828d72019-04-22 14:18:33 +0530712 getLDAPConfigData(serverType, [this, asyncResp, userName, password,
713 baseDNList, userNameAttribute,
714 groupsAttribute, accountProviderType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530715 serviceAddressList, serviceEnabled,
716 dbusObjectPath](
Ratan Guptaab828d72019-04-22 14:18:33 +0530717 bool success, LDAPConfigData confData,
718 const std::string& serverType) {
719 if (!success)
720 {
721 messages::internalError(asyncResp->res);
722 return;
723 }
724 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverType);
725 if (confData.serviceEnabled)
726 {
727 // Disable the service first and update the rest of
728 // the properties.
729 handleServiceEnablePatch(false, asyncResp, serverType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530730 dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530731 }
Ratan Gupta8a07d282019-03-16 08:33:47 +0530732
Ratan Guptaab828d72019-04-22 14:18:33 +0530733 if (serviceAddressList)
734 {
735 handleServiceAddressPatch(*serviceAddressList, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530736 serverType, dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530737 }
738 if (userName)
739 {
740 handleUserNamePatch(*userName, asyncResp, serverType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530741 dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530742 }
743 if (password)
744 {
745 handlePasswordPatch(*password, asyncResp, serverType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530746 dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530747 }
Ratan Gupta8a07d282019-03-16 08:33:47 +0530748
Ratan Guptaab828d72019-04-22 14:18:33 +0530749 if (baseDNList)
750 {
751 handleBaseDNPatch(*baseDNList, asyncResp, serverType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530752 dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530753 }
754 if (userNameAttribute)
755 {
756 handleUserNameAttrPatch(*userNameAttribute, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530757 serverType, dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530758 }
759 if (groupsAttribute)
760 {
761 handleGroupNameAttrPatch(*groupsAttribute, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530762 serverType, dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530763 }
764 if (serviceEnabled)
765 {
766 // if user has given the value as true then enable
767 // the service. if user has given false then no-op
768 // as service is already stopped.
769 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530770 {
Ratan Guptaab828d72019-04-22 14:18:33 +0530771 handleServiceEnablePatch(*serviceEnabled, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530772 serverType, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +0530773 }
Ratan Guptaab828d72019-04-22 14:18:33 +0530774 }
775 else
776 {
777 // if user has not given the service enabled value
778 // then revert it to the same state as it was
779 // before.
780 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530781 serverType, dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530782 }
783 });
Ratan Gupta8a07d282019-03-16 08:33:47 +0530784 }
785
Ed Tanous1abe55e2018-09-05 08:30:59 -0700786 void doGet(crow::Response& res, const crow::Request& req,
787 const std::vector<std::string>& params) override
788 {
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530789 auto asyncResp = std::make_shared<AsyncResp>(res);
790 res.jsonValue = {
791 {"@odata.context", "/redfish/v1/"
792 "$metadata#AccountService.AccountService"},
793 {"@odata.id", "/redfish/v1/AccountService"},
794 {"@odata.type", "#AccountService."
Ratan Gupta6973a582018-12-13 18:25:44 +0530795 "v1_3_1.AccountService"},
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530796 {"Id", "AccountService"},
797 {"Name", "Account Service"},
798 {"Description", "Account Service"},
799 {"ServiceEnabled", true},
AppaRao Puli343ff2e2019-03-24 00:42:13 +0530800 {"MaxPasswordLength", 20},
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530801 {"Accounts",
802 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
803 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}}};
Ed Tanous0f74e642018-11-12 15:17:05 -0800804
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530805 crow::connections::systemBus->async_method_call(
806 [asyncResp](
807 const boost::system::error_code ec,
808 const std::vector<std::pair<
Ed Tanousabf2add2019-01-22 16:40:12 -0800809 std::string, std::variant<uint32_t, uint16_t, uint8_t>>>&
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530810 propertiesList) {
811 if (ec)
812 {
813 messages::internalError(asyncResp->res);
814 return;
815 }
816 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
817 << "properties for AccountService";
818 for (const std::pair<std::string,
Ed Tanousabf2add2019-01-22 16:40:12 -0800819 std::variant<uint32_t, uint16_t, uint8_t>>&
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530820 property : propertiesList)
821 {
822 if (property.first == "MinPasswordLength")
823 {
824 const uint8_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800825 std::get_if<uint8_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530826 if (value != nullptr)
827 {
828 asyncResp->res.jsonValue["MinPasswordLength"] =
829 *value;
830 }
831 }
832 if (property.first == "AccountUnlockTimeout")
833 {
834 const uint32_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800835 std::get_if<uint32_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530836 if (value != nullptr)
837 {
838 asyncResp->res.jsonValue["AccountLockoutDuration"] =
839 *value;
840 }
841 }
842 if (property.first == "MaxLoginAttemptBeforeLockout")
843 {
844 const uint16_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800845 std::get_if<uint16_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530846 if (value != nullptr)
847 {
848 asyncResp->res
849 .jsonValue["AccountLockoutThreshold"] = *value;
850 }
851 }
852 }
853 },
854 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
855 "org.freedesktop.DBus.Properties", "GetAll",
856 "xyz.openbmc_project.User.AccountPolicy");
Ratan Gupta6973a582018-12-13 18:25:44 +0530857
Ratan Guptaab828d72019-04-22 14:18:33 +0530858 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
859 const std::string& ldapType) {
860 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
861 };
862
863 getLDAPConfigData("LDAP", callback);
864 getLDAPConfigData("ActiveDirectory", callback);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530865 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530866
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530867 void doPatch(crow::Response& res, const crow::Request& req,
868 const std::vector<std::string>& params) override
869 {
870 auto asyncResp = std::make_shared<AsyncResp>(res);
871
872 std::optional<uint32_t> unlockTimeout;
873 std::optional<uint16_t> lockoutThreshold;
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530874 std::optional<uint16_t> minPasswordLength;
875 std::optional<uint16_t> maxPasswordLength;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530876 std::optional<nlohmann::json> ldapObject;
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530877 std::optional<nlohmann::json> activeDirectoryObject;
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530878
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530879 if (!json_util::readJson(req, res, "AccountLockoutDuration",
880 unlockTimeout, "AccountLockoutThreshold",
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530881 lockoutThreshold, "MaxPasswordLength",
882 maxPasswordLength, "MinPasswordLength",
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530883 minPasswordLength, "LDAP", ldapObject,
884 "ActiveDirectory", activeDirectoryObject))
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530885 {
886 return;
887 }
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530888
889 if (minPasswordLength)
890 {
891 messages::propertyNotWritable(asyncResp->res, "MinPasswordLength");
892 }
893
894 if (maxPasswordLength)
895 {
896 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
897 }
898
Ratan Gupta8a07d282019-03-16 08:33:47 +0530899 if (ldapObject)
900 {
901 handleLDAPPatch(*ldapObject, asyncResp, req, params, "LDAP");
902 }
903
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530904 if (activeDirectoryObject)
905 {
906 handleLDAPPatch(*activeDirectoryObject, asyncResp, req, params,
907 "ActiveDirectory");
908 }
909
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530910 if (unlockTimeout)
911 {
912 crow::connections::systemBus->async_method_call(
913 [asyncResp](const boost::system::error_code ec) {
914 if (ec)
915 {
916 messages::internalError(asyncResp->res);
917 return;
918 }
Ratan Guptaadd61332019-02-13 20:49:16 +0530919 messages::success(asyncResp->res);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530920 },
921 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
922 "org.freedesktop.DBus.Properties", "Set",
923 "xyz.openbmc_project.User.AccountPolicy",
Ed Tanousabf2add2019-01-22 16:40:12 -0800924 "AccountUnlockTimeout", std::variant<uint32_t>(*unlockTimeout));
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530925 }
926 if (lockoutThreshold)
927 {
928 crow::connections::systemBus->async_method_call(
929 [asyncResp](const boost::system::error_code ec) {
930 if (ec)
931 {
932 messages::internalError(asyncResp->res);
933 return;
934 }
Ratan Guptaadd61332019-02-13 20:49:16 +0530935 messages::success(asyncResp->res);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530936 },
937 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
938 "org.freedesktop.DBus.Properties", "Set",
939 "xyz.openbmc_project.User.AccountPolicy",
940 "MaxLoginAttemptBeforeLockout",
Ed Tanousabf2add2019-01-22 16:40:12 -0800941 std::variant<uint16_t>(*lockoutThreshold));
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530942 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700943 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +0100944};
Tanousf00032d2018-11-05 01:18:10 -0300945
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700946class AccountsCollection : public Node
947{
948 public:
949 AccountsCollection(CrowApp& app) :
950 Node(app, "/redfish/v1/AccountService/Accounts/")
951 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700952 entityPrivileges = {
953 {boost::beast::http::verb::get,
954 {{"ConfigureUsers"}, {"ConfigureManager"}}},
955 {boost::beast::http::verb::head, {{"Login"}}},
956 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
957 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
958 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
959 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
960 }
961
962 private:
963 void doGet(crow::Response& res, const crow::Request& req,
964 const std::vector<std::string>& params) override
965 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700966 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous0f74e642018-11-12 15:17:05 -0800967 res.jsonValue = {{"@odata.context",
968 "/redfish/v1/"
969 "$metadata#ManagerAccountCollection."
970 "ManagerAccountCollection"},
971 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
972 {"@odata.type", "#ManagerAccountCollection."
973 "ManagerAccountCollection"},
974 {"Name", "Accounts Collection"},
975 {"Description", "BMC User Accounts"}};
976
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700977 crow::connections::systemBus->async_method_call(
978 [asyncResp](const boost::system::error_code ec,
979 const ManagedObjectType& users) {
980 if (ec)
981 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700982 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700983 return;
984 }
985
986 nlohmann::json& memberArray =
987 asyncResp->res.jsonValue["Members"];
988 memberArray = nlohmann::json::array();
989
990 asyncResp->res.jsonValue["Members@odata.count"] = users.size();
991 for (auto& user : users)
992 {
993 const std::string& path =
994 static_cast<const std::string&>(user.first);
995 std::size_t lastIndex = path.rfind("/");
996 if (lastIndex == std::string::npos)
997 {
998 lastIndex = 0;
999 }
1000 else
1001 {
1002 lastIndex += 1;
1003 }
1004 memberArray.push_back(
1005 {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
1006 path.substr(lastIndex)}});
1007 }
1008 },
1009 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1010 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1011 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001012 void doPost(crow::Response& res, const crow::Request& req,
1013 const std::vector<std::string>& params) override
1014 {
1015 auto asyncResp = std::make_shared<AsyncResp>(res);
1016
Ed Tanous9712f8a2018-09-21 13:38:49 -07001017 std::string username;
1018 std::string password;
Ed Tanousa24526d2018-12-10 15:17:59 -08001019 std::optional<std::string> roleId("User");
1020 std::optional<bool> enabled = true;
Ed Tanous9712f8a2018-09-21 13:38:49 -07001021 if (!json_util::readJson(req, res, "UserName", username, "Password",
1022 password, "RoleId", roleId, "Enabled",
1023 enabled))
Ed Tanous04ae99e2018-09-20 15:54:36 -07001024 {
1025 return;
1026 }
1027
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301028 std::string priv = getRoleIdFromPrivilege(*roleId);
1029 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001030 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001031 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001032 return;
1033 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001034 roleId = priv;
Ed Tanous04ae99e2018-09-20 15:54:36 -07001035
1036 crow::connections::systemBus->async_method_call(
Ed Tanous9712f8a2018-09-21 13:38:49 -07001037 [asyncResp, username, password{std::move(password)}](
Ed Tanous04ae99e2018-09-20 15:54:36 -07001038 const boost::system::error_code ec) {
1039 if (ec)
1040 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001041 messages::resourceAlreadyExists(
1042 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
1043 "UserName", username);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001044 return;
1045 }
1046
1047 if (!pamUpdatePassword(username, password))
1048 {
1049 // At this point we have a user that's been created, but the
1050 // password set failed. Something is wrong, so delete the
1051 // user that we've already created
1052 crow::connections::systemBus->async_method_call(
1053 [asyncResp](const boost::system::error_code ec) {
1054 if (ec)
1055 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001056 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001057 return;
1058 }
1059
Jason M. Billsf12894f2018-10-09 12:45:45 -07001060 messages::invalidObject(asyncResp->res, "Password");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001061 },
1062 "xyz.openbmc_project.User.Manager",
1063 "/xyz/openbmc_project/user/" + username,
1064 "xyz.openbmc_project.Object.Delete", "Delete");
1065
1066 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1067 return;
1068 }
1069
Jason M. Billsf12894f2018-10-09 12:45:45 -07001070 messages::created(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001071 asyncResp->res.addHeader(
1072 "Location",
1073 "/redfish/v1/AccountService/Accounts/" + username);
1074 },
1075 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ed Tanous9712f8a2018-09-21 13:38:49 -07001076 "xyz.openbmc_project.User.Manager", "CreateUser", username,
Ed Tanous04ae99e2018-09-20 15:54:36 -07001077 std::array<const char*, 4>{"ipmi", "redfish", "ssh", "web"},
Ed Tanous9712f8a2018-09-21 13:38:49 -07001078 *roleId, *enabled);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001079 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001080};
1081
1082class ManagerAccount : public Node
1083{
1084 public:
1085 ManagerAccount(CrowApp& app) :
1086 Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
1087 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001088 entityPrivileges = {
1089 {boost::beast::http::verb::get,
1090 {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
1091 {boost::beast::http::verb::head, {{"Login"}}},
1092 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
1093 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
1094 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
1095 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
1096 }
1097
1098 private:
1099 void doGet(crow::Response& res, const crow::Request& req,
1100 const std::vector<std::string>& params) override
1101 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001102 res.jsonValue = {
1103 {"@odata.context",
1104 "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
1105 {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
Ed Tanous0f74e642018-11-12 15:17:05 -08001106 {"Name", "User Account"},
1107 {"Description", "User Account"},
1108 {"Password", nullptr},
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301109 {"RoleId", "Administrator"}};
Ed Tanous0f74e642018-11-12 15:17:05 -08001110
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001111 auto asyncResp = std::make_shared<AsyncResp>(res);
1112
1113 if (params.size() != 1)
1114 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001115 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001116 return;
1117 }
1118
1119 crow::connections::systemBus->async_method_call(
1120 [asyncResp, accountName{std::string(params[0])}](
1121 const boost::system::error_code ec,
1122 const ManagedObjectType& users) {
1123 if (ec)
1124 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001125 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001126 return;
1127 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301128 auto userIt = users.begin();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001129
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301130 for (; userIt != users.end(); userIt++)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001131 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301132 if (boost::ends_with(userIt->first.str, "/" + accountName))
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001133 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301134 break;
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001135 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301136 }
1137 if (userIt == users.end())
1138 {
1139 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1140 accountName);
1141 return;
1142 }
1143 for (const auto& interface : userIt->second)
1144 {
1145 if (interface.first ==
1146 "xyz.openbmc_project.User.Attributes")
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001147 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301148 for (const auto& property : interface.second)
Ed Tanous65b0dc32018-09-19 16:04:03 -07001149 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301150 if (property.first == "UserEnabled")
Ed Tanous65b0dc32018-09-19 16:04:03 -07001151 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301152 const bool* userEnabled =
Ed Tanousabf2add2019-01-22 16:40:12 -08001153 std::get_if<bool>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301154 if (userEnabled == nullptr)
Ed Tanous65b0dc32018-09-19 16:04:03 -07001155 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301156 BMCWEB_LOG_ERROR
1157 << "UserEnabled wasn't a bool";
1158 messages::internalError(asyncResp->res);
1159 return;
Ed Tanous65b0dc32018-09-19 16:04:03 -07001160 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301161 asyncResp->res.jsonValue["Enabled"] =
1162 *userEnabled;
1163 }
1164 else if (property.first ==
1165 "UserLockedForFailedAttempt")
1166 {
1167 const bool* userLocked =
Ed Tanousabf2add2019-01-22 16:40:12 -08001168 std::get_if<bool>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301169 if (userLocked == nullptr)
1170 {
1171 BMCWEB_LOG_ERROR << "UserLockedForF"
1172 "ailedAttempt "
1173 "wasn't a bool";
1174 messages::internalError(asyncResp->res);
1175 return;
1176 }
1177 asyncResp->res.jsonValue["Locked"] =
1178 *userLocked;
Ratan Gupta24c85422019-01-30 19:41:24 +05301179 asyncResp->res.jsonValue
1180 ["Locked@Redfish.AllowableValues"] = {
Gunnar Mills4d64ce32019-03-29 16:34:56 -05001181 "false"};
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301182 }
1183 else if (property.first == "UserPrivilege")
1184 {
1185 const std::string* userRolePtr =
Ed Tanousabf2add2019-01-22 16:40:12 -08001186 std::get_if<std::string>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301187 if (userRolePtr == nullptr)
1188 {
1189 BMCWEB_LOG_ERROR
1190 << "UserPrivilege wasn't a "
1191 "string";
1192 messages::internalError(asyncResp->res);
1193 return;
1194 }
1195 std::string priv =
1196 getPrivilegeFromRoleId(*userRolePtr);
1197 if (priv.empty())
1198 {
1199 BMCWEB_LOG_ERROR << "Invalid user role";
1200 messages::internalError(asyncResp->res);
1201 return;
1202 }
1203 asyncResp->res.jsonValue["RoleId"] = priv;
1204
1205 asyncResp->res.jsonValue["Links"]["Role"] = {
1206 {"@odata.id", "/redfish/v1/AccountService/"
1207 "Roles/" +
1208 priv}};
Ed Tanous65b0dc32018-09-19 16:04:03 -07001209 }
1210 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001211 }
1212 }
1213
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301214 asyncResp->res.jsonValue["@odata.id"] =
1215 "/redfish/v1/AccountService/Accounts/" + accountName;
1216 asyncResp->res.jsonValue["Id"] = accountName;
1217 asyncResp->res.jsonValue["UserName"] = accountName;
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001218 },
1219 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1220 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1221 }
Ed Tanousa8408792018-09-05 16:08:38 -07001222
1223 void doPatch(crow::Response& res, const crow::Request& req,
1224 const std::vector<std::string>& params) override
1225 {
1226 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousa8408792018-09-05 16:08:38 -07001227 if (params.size() != 1)
1228 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001229 messages::internalError(asyncResp->res);
Ed Tanousa8408792018-09-05 16:08:38 -07001230 return;
1231 }
1232
Ed Tanousa24526d2018-12-10 15:17:59 -08001233 std::optional<std::string> newUserName;
1234 std::optional<std::string> password;
1235 std::optional<bool> enabled;
1236 std::optional<std::string> roleId;
Ratan Gupta24c85422019-01-30 19:41:24 +05301237 std::optional<bool> locked;
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301238 if (!json_util::readJson(req, res, "UserName", newUserName, "Password",
Ratan Gupta24c85422019-01-30 19:41:24 +05301239 password, "RoleId", roleId, "Enabled", enabled,
1240 "Locked", locked))
Ed Tanousa8408792018-09-05 16:08:38 -07001241 {
1242 return;
1243 }
1244
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301245 const std::string& username = params[0];
Ed Tanousa8408792018-09-05 16:08:38 -07001246
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301247 if (!newUserName)
1248 {
1249 // If the username isn't being updated, we can update the properties
1250 // directly
Ratan Gupta24c85422019-01-30 19:41:24 +05301251 updateUserProperties(asyncResp, username, password, enabled, roleId,
1252 locked);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301253 return;
1254 }
1255 else
1256 {
1257 crow::connections::systemBus->async_method_call(
1258 [this, asyncResp, username, password(std::move(password)),
1259 roleId(std::move(roleId)), enabled(std::move(enabled)),
Ratan Gupta24c85422019-01-30 19:41:24 +05301260 newUser{std::string(*newUserName)}, locked(std::move(locked))](
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301261 const boost::system::error_code ec) {
1262 if (ec)
Ed Tanousa8408792018-09-05 16:08:38 -07001263 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301264 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1265 messages::resourceNotFound(
1266 asyncResp->res,
1267 "#ManagerAccount.v1_0_3.ManagerAccount", username);
1268 return;
1269 }
1270
1271 updateUserProperties(asyncResp, newUser, password, enabled,
Ratan Gupta24c85422019-01-30 19:41:24 +05301272 roleId, locked);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301273 },
1274 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1275 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1276 *newUserName);
1277 }
1278 }
1279
1280 void updateUserProperties(std::shared_ptr<AsyncResp> asyncResp,
1281 const std::string& username,
Ed Tanousa24526d2018-12-10 15:17:59 -08001282 std::optional<std::string> password,
1283 std::optional<bool> enabled,
Ratan Gupta24c85422019-01-30 19:41:24 +05301284 std::optional<std::string> roleId,
1285 std::optional<bool> locked)
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301286 {
1287 if (password)
1288 {
1289 if (!pamUpdatePassword(username, *password))
1290 {
1291 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1292 messages::internalError(asyncResp->res);
1293 return;
1294 }
1295 }
1296
Ratan Gupta24c85422019-01-30 19:41:24 +05301297 std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
1298 dbus::utility::escapePathForDbus(dbusObjectPath);
1299
Ratan Gupta22c33712019-05-03 21:50:28 +05301300 dbus::utility::checkDbusPathExists(
Ratan Gupta24c85422019-01-30 19:41:24 +05301301 dbusObjectPath,
1302 [dbusObjectPath(std::move(dbusObjectPath)), username,
1303 password(std::move(password)), roleId(std::move(roleId)),
1304 enabled(std::move(enabled)), locked(std::move(locked)),
1305 asyncResp{std::move(asyncResp)}](int rc) {
1306 if (!rc)
1307 {
1308 messages::invalidObject(asyncResp->res, username.c_str());
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301309 return;
Ratan Gupta24c85422019-01-30 19:41:24 +05301310 }
1311 if (enabled)
1312 {
1313 crow::connections::systemBus->async_method_call(
1314 [asyncResp](const boost::system::error_code ec) {
1315 if (ec)
1316 {
1317 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1318 << ec;
1319 messages::internalError(asyncResp->res);
1320 return;
1321 }
1322 messages::success(asyncResp->res);
1323 return;
1324 },
1325 "xyz.openbmc_project.User.Manager",
1326 dbusObjectPath.c_str(),
1327 "org.freedesktop.DBus.Properties", "Set",
1328 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1329 std::variant<bool>{*enabled});
1330 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001331
Ratan Gupta24c85422019-01-30 19:41:24 +05301332 if (roleId)
1333 {
1334 std::string priv = getRoleIdFromPrivilege(*roleId);
1335 if (priv.empty())
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301336 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301337 messages::propertyValueNotInList(asyncResp->res,
1338 *roleId, "RoleId");
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301339 return;
1340 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301341
1342 crow::connections::systemBus->async_method_call(
1343 [asyncResp](const boost::system::error_code ec) {
1344 if (ec)
1345 {
1346 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1347 << ec;
1348 messages::internalError(asyncResp->res);
1349 return;
1350 }
1351 messages::success(asyncResp->res);
1352 },
1353 "xyz.openbmc_project.User.Manager",
1354 dbusObjectPath.c_str(),
1355 "org.freedesktop.DBus.Properties", "Set",
1356 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1357 std::variant<std::string>{priv});
1358 }
1359
1360 if (locked)
1361 {
1362 // admin can unlock the account which is locked by
1363 // successive authentication failures but admin should not
1364 // be allowed to lock an account.
1365 if (*locked)
1366 {
1367 messages::propertyValueNotInList(asyncResp->res, "true",
1368 "Locked");
1369 return;
1370 }
1371
1372 crow::connections::systemBus->async_method_call(
1373 [asyncResp](const boost::system::error_code ec) {
1374 if (ec)
1375 {
1376 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1377 << ec;
1378 messages::internalError(asyncResp->res);
1379 return;
1380 }
1381 messages::success(asyncResp->res);
1382 return;
1383 },
1384 "xyz.openbmc_project.User.Manager",
1385 dbusObjectPath.c_str(),
1386 "org.freedesktop.DBus.Properties", "Set",
1387 "xyz.openbmc_project.User.Attributes",
1388 "UserLockedForFailedAttempt",
1389 sdbusplus::message::variant<bool>{*locked});
1390 }
1391 });
Ed Tanousa8408792018-09-05 16:08:38 -07001392 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001393
1394 void doDelete(crow::Response& res, const crow::Request& req,
1395 const std::vector<std::string>& params) override
1396 {
1397 auto asyncResp = std::make_shared<AsyncResp>(res);
1398
1399 if (params.size() != 1)
1400 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001401 messages::internalError(asyncResp->res);
Ed Tanous06e086d2018-09-19 17:19:52 -07001402 return;
1403 }
1404
1405 const std::string userPath = "/xyz/openbmc_project/user/" + params[0];
1406
1407 crow::connections::systemBus->async_method_call(
1408 [asyncResp, username{std::move(params[0])}](
1409 const boost::system::error_code ec) {
1410 if (ec)
1411 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001412 messages::resourceNotFound(
1413 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
1414 username);
Ed Tanous06e086d2018-09-19 17:19:52 -07001415 return;
1416 }
1417
Jason M. Billsf12894f2018-10-09 12:45:45 -07001418 messages::accountRemoved(asyncResp->res);
Ed Tanous06e086d2018-09-19 17:19:52 -07001419 },
1420 "xyz.openbmc_project.User.Manager", userPath,
1421 "xyz.openbmc_project.Object.Delete", "Delete");
1422 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301423};
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001424
Ed Tanous1abe55e2018-09-05 08:30:59 -07001425} // namespace redfish