blob: 784cbd28e879850f3bff8225a85abe3ff04c838c [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";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600113 nlohmann::json ldap = {
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 };
Marri Devender Rao37cce912019-02-20 01:05:22 -0600128 json_response[ldapType].update(std::move(ldap));
Ratan Gupta6973a582018-12-13 18:25:44 +0530129}
130
131/**
132 * Function that retrieves all properties for LDAP config object
133 * into JSON
134 */
135template <typename CallbackFunc>
136inline void getLDAPConfigData(const std::string& ldapType,
137 CallbackFunc&& callback)
138{
139 auto getConfig = [callback,
140 ldapType](const boost::system::error_code error_code,
141 const ManagedObjectType& ldapObjects) {
142 LDAPConfigData confData{};
143 if (error_code)
144 {
Ratan Guptaab828d72019-04-22 14:18:33 +0530145 callback(false, confData, ldapType);
Ratan Gupta6973a582018-12-13 18:25:44 +0530146 BMCWEB_LOG_ERROR << "D-Bus responses error: " << error_code;
147 return;
148 }
Ratan Guptaab828d72019-04-22 14:18:33 +0530149
150 std::string ldapDbusType;
151 if (ldapType == "LDAP")
152 {
153 ldapDbusType = "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
154 }
155 else if (ldapType == "ActiveDirectory")
156 {
157 ldapDbusType = "xyz.openbmc_project.User.Ldap.Config.Type."
158 "ActiveDirectory";
159 }
160 else
161 {
162 BMCWEB_LOG_ERROR << "Can't get the DbusType for the given type="
163 << ldapType;
164 callback(false, confData, ldapType);
165 return;
166 }
167
168 std::string ldapEnableInterfaceStr = ldapEnableInterface;
169 std::string ldapConfigInterfaceStr = ldapConfigInterface;
170
Ratan Gupta6973a582018-12-13 18:25:44 +0530171 for (const auto& object : ldapObjects)
172 {
Ratan Guptaab828d72019-04-22 14:18:33 +0530173 // let's find the object whose ldap type is equal to the given type
174 auto intfit = object.second.find(ldapConfigInterfaceStr);
175 if (intfit == object.second.end())
Ratan Gupta6973a582018-12-13 18:25:44 +0530176 {
Ratan Guptaab828d72019-04-22 14:18:33 +0530177 continue;
178 }
179 auto propit = intfit->second.find("LDAPType");
180 if (propit == intfit->second.end())
181 {
182 continue;
183 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530184
Ratan Guptaab828d72019-04-22 14:18:33 +0530185 const std::string* value =
186 std::get_if<std::string>(&(propit->second));
187 if (value == nullptr || (*value) != ldapDbusType)
188 {
189
190 // this is not the interested configuration,
191 // let's move on to the other configuration.
192 continue;
193 }
194 else
195 {
196 confData.serverType = *value;
197 }
198
199 for (const auto& interface : object.second)
200 {
201 if (interface.first == ldapEnableInterfaceStr)
202 {
203 // rest of the properties are string.
204 for (const auto& property : interface.second)
205 {
206 if (property.first == "Enabled")
Ratan Gupta6973a582018-12-13 18:25:44 +0530207 {
Ratan Guptaab828d72019-04-22 14:18:33 +0530208 const bool* value =
209 std::get_if<bool>(&property.second);
Ratan Gupta6973a582018-12-13 18:25:44 +0530210 if (value == nullptr)
211 {
212 continue;
213 }
Ratan Guptaab828d72019-04-22 14:18:33 +0530214 confData.serviceEnabled = *value;
215 break;
Ratan Gupta6973a582018-12-13 18:25:44 +0530216 }
217 }
218 }
Ratan Guptaab828d72019-04-22 14:18:33 +0530219 else if (interface.first == ldapConfigInterfaceStr)
220 {
Ratan Gupta6973a582018-12-13 18:25:44 +0530221
Ratan Guptaab828d72019-04-22 14:18:33 +0530222 for (const auto& property : interface.second)
223 {
224 const std::string* value =
225 std::get_if<std::string>(&property.second);
226 if (value == nullptr)
227 {
228 continue;
229 }
230 if (property.first == "LDAPServerURI")
231 {
232 confData.uri = *value;
233 }
234 else if (property.first == "LDAPBindDN")
235 {
236 confData.bindDN = *value;
237 }
238 else if (property.first == "LDAPBaseDN")
239 {
240 confData.baseDN = *value;
241 }
242 else if (property.first == "LDAPSearchScope")
243 {
244 confData.searchScope = *value;
245 }
246 else if (property.first == "GroupNameAttribute")
247 {
248 confData.groupAttribute = *value;
249 }
250 else if (property.first == "UserNameAttribute")
251 {
252 confData.userNameAttribute = *value;
253 }
254 }
255 }
256 }
257 if (confData.serverType == ldapDbusType)
258 {
259 callback(true, confData, ldapType);
Ratan Gupta6973a582018-12-13 18:25:44 +0530260 break;
261 }
262 }
263 };
Ratan Guptaab828d72019-04-22 14:18:33 +0530264 auto getServiceName = [callback, ldapType, getConfig(std::move(getConfig))](
Ratan Gupta6973a582018-12-13 18:25:44 +0530265 const boost::system::error_code ec,
266 const GetObjectType& resp) {
267 LDAPConfigData confData{};
268 if (ec || resp.empty())
269 {
270 BMCWEB_LOG_ERROR
271 << "DBUS response error during getting of service name: " << ec;
Ratan Guptaab828d72019-04-22 14:18:33 +0530272 callback(false, confData, ldapType);
Ratan Gupta6973a582018-12-13 18:25:44 +0530273 return;
274 }
275 std::string service = resp.begin()->first;
276 crow::connections::systemBus->async_method_call(
277 std::move(getConfig), service, ldapRootObject, dbusObjManagerIntf,
278 "GetManagedObjects");
279 };
280
281 const std::array<std::string, 2> interfaces = {ldapEnableInterface,
282 ldapConfigInterface};
283
284 crow::connections::systemBus->async_method_call(
285 std::move(getServiceName), mapperBusName, mapperObjectPath, mapperIntf,
286 "GetObject", ldapConfigObject, interfaces);
287}
288
Ed Tanous1abe55e2018-09-05 08:30:59 -0700289class AccountService : public Node
290{
291 public:
292 AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/")
293 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 entityPrivileges = {
295 {boost::beast::http::verb::get,
296 {{"ConfigureUsers"}, {"ConfigureManager"}}},
297 {boost::beast::http::verb::head, {{"Login"}}},
298 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
299 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
300 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
301 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
302 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +0100303
Ed Tanous1abe55e2018-09-05 08:30:59 -0700304 private:
Ratan Gupta8a07d282019-03-16 08:33:47 +0530305 /**
306 * @brief parses the authentication section under the LDAP
307 * @param input JSON data
308 * @param asyncResp pointer to the JSON response
309 * @param userName userName to be filled from the given JSON.
310 * @param password password to be filled from the given JSON.
311 */
312 void
313 parseLDAPAuthenticationJson(nlohmann::json input,
314 const std::shared_ptr<AsyncResp>& asyncResp,
315 std::optional<std::string>& username,
316 std::optional<std::string>& password)
317 {
318 std::optional<std::string> authType;
319
320 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
321 authType, "Username", username, "Password",
322 password))
323 {
324 return;
325 }
326 if (!authType)
327 {
328 return;
329 }
330 if (*authType != "UsernameAndPassword")
331 {
332 messages::propertyValueNotInList(asyncResp->res, *authType,
333 "AuthenticationType");
334 return;
335 }
336 }
337 /**
338 * @brief parses the LDAPService section under the LDAP
339 * @param input JSON data
340 * @param asyncResp pointer to the JSON response
341 * @param baseDNList baseDN to be filled from the given JSON.
342 * @param userNameAttribute userName to be filled from the given JSON.
343 * @param groupaAttribute password to be filled from the given JSON.
344 */
345
346 void parseLDAPServiceJson(
347 nlohmann::json input, const std::shared_ptr<AsyncResp>& asyncResp,
348 std::optional<std::vector<std::string>>& baseDNList,
349 std::optional<std::string>& userNameAttribute,
350 std::optional<std::string>& groupsAttribute)
351 {
352 std::optional<nlohmann::json> searchSettings;
353
354 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
355 searchSettings))
356 {
357 return;
358 }
359 if (!searchSettings)
360 {
361 return;
362 }
363 if (!json_util::readJson(*searchSettings, asyncResp->res,
364 "BaseDistinguishedNames", baseDNList,
365 "UsernameAttribute", userNameAttribute,
366 "GroupsAttribute", groupsAttribute))
367 {
368 return;
369 }
370 }
371 /**
372 * @brief updates the LDAP server address and updates the
373 json response with the new value.
374 * @param serviceAddressList address to be updated.
375 * @param asyncResp pointer to the JSON response
376 * @param ldapServerElementName Type of LDAP
377 server(openLDAP/ActiveDirectory)
378 */
379
380 void handleServiceAddressPatch(
381 const std::vector<std::string>& serviceAddressList,
382 const std::shared_ptr<AsyncResp>& asyncResp,
383 const std::string& ldapServerElementName,
384 const std::string& ldapConfigObject)
385 {
386 crow::connections::systemBus->async_method_call(
387 [asyncResp, ldapServerElementName,
388 serviceAddressList](const boost::system::error_code ec) {
389 if (ec)
390 {
391 BMCWEB_LOG_DEBUG
392 << "Error Occured in updating the service address";
393 messages::internalError(asyncResp->res);
394 return;
395 }
396 std::vector<std::string> modifiedserviceAddressList = {
397 serviceAddressList.front()};
398 asyncResp->res
399 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
400 modifiedserviceAddressList;
401 if ((serviceAddressList).size() > 1)
402 {
403 messages::propertyValueModified(asyncResp->res,
404 "ServiceAddresses",
405 serviceAddressList.front());
406 }
407 BMCWEB_LOG_DEBUG << "Updated the service address";
408 },
409 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
410 ldapConfigInterface, "LDAPServerURI",
411 std::variant<std::string>(serviceAddressList.front()));
412 }
413 /**
414 * @brief updates the LDAP Bind DN and updates the
415 json response with the new value.
416 * @param username name of the user which needs to be updated.
417 * @param asyncResp pointer to the JSON response
418 * @param ldapServerElementName Type of LDAP
419 server(openLDAP/ActiveDirectory)
420 */
421
422 void handleUserNamePatch(const std::string& username,
423 const std::shared_ptr<AsyncResp>& asyncResp,
424 const std::string& ldapServerElementName,
425 const std::string& ldapConfigObject)
426 {
427 crow::connections::systemBus->async_method_call(
428 [asyncResp, username,
429 ldapServerElementName](const boost::system::error_code ec) {
430 if (ec)
431 {
432 BMCWEB_LOG_DEBUG
433 << "Error occured in updating the username";
434 messages::internalError(asyncResp->res);
435 return;
436 }
437 asyncResp->res.jsonValue[ldapServerElementName]
438 ["Authentication"]["Username"] =
439 username;
440 BMCWEB_LOG_DEBUG << "Updated the username";
441 },
442 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
443 ldapConfigInterface, "LDAPBindDN",
444 std::variant<std::string>(username));
445 }
446
447 /**
448 * @brief updates the LDAP password
449 * @param password : ldap password which needs to be updated.
450 * @param asyncResp pointer to the JSON response
451 * @param ldapServerElementName Type of LDAP
452 * server(openLDAP/ActiveDirectory)
453 */
454
455 void handlePasswordPatch(const std::string& password,
456 const std::shared_ptr<AsyncResp>& asyncResp,
457 const std::string& ldapServerElementName,
458 const std::string& ldapConfigObject)
459 {
460 crow::connections::systemBus->async_method_call(
461 [asyncResp, password,
462 ldapServerElementName](const boost::system::error_code ec) {
463 if (ec)
464 {
465 BMCWEB_LOG_DEBUG
466 << "Error occured in updating the password";
467 messages::internalError(asyncResp->res);
468 return;
469 }
470 asyncResp->res.jsonValue[ldapServerElementName]
471 ["Authentication"]["Password"] = "";
472 BMCWEB_LOG_DEBUG << "Updated the password";
473 },
474 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
475 ldapConfigInterface, "LDAPBindDNPassword",
476 std::variant<std::string>(password));
477 }
478
479 /**
480 * @brief updates the LDAP BaseDN and updates the
481 json response with the new value.
482 * @param baseDNList baseDN list which needs to be updated.
483 * @param asyncResp pointer to the JSON response
484 * @param ldapServerElementName Type of LDAP
485 server(openLDAP/ActiveDirectory)
486 */
487
488 void handleBaseDNPatch(const std::vector<std::string>& baseDNList,
489 const std::shared_ptr<AsyncResp>& asyncResp,
490 const std::string& ldapServerElementName,
491 const std::string& ldapConfigObject)
492 {
493 crow::connections::systemBus->async_method_call(
494 [asyncResp, baseDNList,
495 ldapServerElementName](const boost::system::error_code ec) {
496 if (ec)
497 {
498 BMCWEB_LOG_DEBUG << "Error Occured in Updating the base DN";
499 messages::internalError(asyncResp->res);
500 return;
501 }
502 auto& serverTypeJson =
503 asyncResp->res.jsonValue[ldapServerElementName];
504 auto& searchSettingsJson =
505 serverTypeJson["LDAPService"]["SearchSettings"];
506 std::vector<std::string> modifiedBaseDNList = {
507 baseDNList.front()};
508 searchSettingsJson["BaseDistinguishedNames"] =
509 modifiedBaseDNList;
510 if (baseDNList.size() > 1)
511 {
512 messages::propertyValueModified(asyncResp->res,
513 "BaseDistinguishedNames",
514 baseDNList.front());
515 }
516 BMCWEB_LOG_DEBUG << "Updated the base DN";
517 },
518 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
519 ldapConfigInterface, "LDAPBaseDN",
520 std::variant<std::string>(baseDNList.front()));
521 }
522 /**
523 * @brief updates the LDAP user name attribute and updates the
524 json response with the new value.
525 * @param userNameAttribute attribute to be updated.
526 * @param asyncResp pointer to the JSON response
527 * @param ldapServerElementName Type of LDAP
528 server(openLDAP/ActiveDirectory)
529 */
530
531 void handleUserNameAttrPatch(const std::string& userNameAttribute,
532 const std::shared_ptr<AsyncResp>& asyncResp,
533 const std::string& ldapServerElementName,
534 const std::string& ldapConfigObject)
535 {
536 crow::connections::systemBus->async_method_call(
537 [asyncResp, userNameAttribute,
538 ldapServerElementName](const boost::system::error_code ec) {
539 if (ec)
540 {
541 BMCWEB_LOG_DEBUG << "Error Occured in Updating the "
542 "username attribute";
543 messages::internalError(asyncResp->res);
544 return;
545 }
546 auto& serverTypeJson =
547 asyncResp->res.jsonValue[ldapServerElementName];
548 auto& searchSettingsJson =
549 serverTypeJson["LDAPService"]["SearchSettings"];
550 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
551 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
552 },
553 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
554 ldapConfigInterface, "UserNameAttribute",
555 std::variant<std::string>(userNameAttribute));
556 }
557 /**
558 * @brief updates the LDAP group attribute and updates the
559 json response with the new value.
560 * @param groupsAttribute attribute to be updated.
561 * @param asyncResp pointer to the JSON response
562 * @param ldapServerElementName Type of LDAP
563 server(openLDAP/ActiveDirectory)
564 */
565
566 void handleGroupNameAttrPatch(const std::string& groupsAttribute,
567 const std::shared_ptr<AsyncResp>& asyncResp,
568 const std::string& ldapServerElementName,
569 const std::string& ldapConfigObject)
570 {
571 crow::connections::systemBus->async_method_call(
572 [asyncResp, groupsAttribute,
573 ldapServerElementName](const boost::system::error_code ec) {
574 if (ec)
575 {
576 BMCWEB_LOG_DEBUG << "Error Occured in Updating the "
577 "groupname attribute";
578 messages::internalError(asyncResp->res);
579 return;
580 }
581 auto& serverTypeJson =
582 asyncResp->res.jsonValue[ldapServerElementName];
583 auto& searchSettingsJson =
584 serverTypeJson["LDAPService"]["SearchSettings"];
585 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
586 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
587 },
588 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
589 ldapConfigInterface, "GroupNameAttribute",
590 std::variant<std::string>(groupsAttribute));
591 }
592 /**
593 * @brief updates the LDAP service enable and updates the
594 json response with the new value.
595 * @param input JSON data.
596 * @param asyncResp pointer to the JSON response
597 * @param ldapServerElementName Type of LDAP
598 server(openLDAP/ActiveDirectory)
599 */
600
601 void handleServiceEnablePatch(bool serviceEnabled,
602 const std::shared_ptr<AsyncResp>& asyncResp,
603 const std::string& ldapServerElementName,
604 const std::string& ldapConfigObject)
605 {
606 crow::connections::systemBus->async_method_call(
607 [asyncResp, serviceEnabled,
608 ldapServerElementName](const boost::system::error_code ec) {
609 if (ec)
610 {
611 BMCWEB_LOG_DEBUG
612 << "Error Occured in Updating the service enable";
613 messages::internalError(asyncResp->res);
614 return;
615 }
616 asyncResp->res
617 .jsonValue[ldapServerElementName]["ServiceEnabled"] =
618 serviceEnabled;
619 BMCWEB_LOG_DEBUG << "Updated Service enable = "
620 << serviceEnabled;
621 },
622 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
623 ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
624 }
625
626 /**
627 * @brief Get the required values from the given JSON, validates the
628 * value and create the LDAP config object.
629 * @param input JSON data
630 * @param asyncResp pointer to the JSON response
631 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
632 */
633
634 void handleLDAPPatch(nlohmann::json& input,
635 const std::shared_ptr<AsyncResp>& asyncResp,
636 const crow::Request& req,
637 const std::vector<std::string>& params,
638 const std::string& serverType)
639 {
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530640 std::string dbusObjectPath;
641 if (serverType == "ActiveDirectory")
642 {
643 dbusObjectPath = ADConfigObject;
644 }
645 else if (serverType == "LDAP")
646 {
647 dbusObjectPath = ldapConfigObject;
648 }
649
Ratan Gupta8a07d282019-03-16 08:33:47 +0530650 std::optional<nlohmann::json> authentication;
651 std::optional<nlohmann::json> ldapService;
652 std::optional<std::string> accountProviderType;
653 std::optional<std::vector<std::string>> serviceAddressList;
654 std::optional<bool> serviceEnabled;
655 std::optional<std::vector<std::string>> baseDNList;
656 std::optional<std::string> userNameAttribute;
657 std::optional<std::string> groupsAttribute;
658 std::optional<std::string> userName;
659 std::optional<std::string> password;
660
661 if (!json_util::readJson(input, asyncResp->res, "Authentication",
662 authentication, "LDAPService", ldapService,
663 "ServiceAddresses", serviceAddressList,
664 "AccountProviderType", accountProviderType,
665 "ServiceEnabled", serviceEnabled))
666 {
667 return;
668 }
669
670 if (authentication)
671 {
672 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
673 password);
674 }
675 if (ldapService)
676 {
677 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
678 userNameAttribute, groupsAttribute);
679 }
680 if (accountProviderType)
681 {
682 messages::propertyNotWritable(asyncResp->res,
683 "AccountProviderType");
684 }
685 if (serviceAddressList)
686 {
687 if ((*serviceAddressList).size() == 0)
688 {
689 messages::propertyValueNotInList(asyncResp->res, "[]",
690 "ServiceAddress");
691 return;
692 }
693 }
694 if (baseDNList)
695 {
696 if ((*baseDNList).size() == 0)
697 {
698 messages::propertyValueNotInList(asyncResp->res, "[]",
699 "BaseDistinguishedNames");
700 return;
701 }
702 }
703
704 // nothing to update, then return
705 if (!userName && !password && !serviceAddressList && !baseDNList &&
706 !userNameAttribute && !groupsAttribute && !serviceEnabled)
707 {
708 return;
709 }
710
711 // Get the existing resource first then keep modifying
712 // whenever any property gets updated.
Ratan Guptaab828d72019-04-22 14:18:33 +0530713 getLDAPConfigData(serverType, [this, asyncResp, userName, password,
714 baseDNList, userNameAttribute,
715 groupsAttribute, accountProviderType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530716 serviceAddressList, serviceEnabled,
717 dbusObjectPath](
Ratan Guptaab828d72019-04-22 14:18:33 +0530718 bool success, LDAPConfigData confData,
719 const std::string& serverType) {
720 if (!success)
721 {
722 messages::internalError(asyncResp->res);
723 return;
724 }
725 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverType);
726 if (confData.serviceEnabled)
727 {
728 // Disable the service first and update the rest of
729 // the properties.
730 handleServiceEnablePatch(false, asyncResp, serverType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530731 dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530732 }
Ratan Gupta8a07d282019-03-16 08:33:47 +0530733
Ratan Guptaab828d72019-04-22 14:18:33 +0530734 if (serviceAddressList)
735 {
736 handleServiceAddressPatch(*serviceAddressList, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530737 serverType, dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530738 }
739 if (userName)
740 {
741 handleUserNamePatch(*userName, asyncResp, serverType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530742 dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530743 }
744 if (password)
745 {
746 handlePasswordPatch(*password, asyncResp, serverType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530747 dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530748 }
Ratan Gupta8a07d282019-03-16 08:33:47 +0530749
Ratan Guptaab828d72019-04-22 14:18:33 +0530750 if (baseDNList)
751 {
752 handleBaseDNPatch(*baseDNList, asyncResp, serverType,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530753 dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530754 }
755 if (userNameAttribute)
756 {
757 handleUserNameAttrPatch(*userNameAttribute, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530758 serverType, dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530759 }
760 if (groupsAttribute)
761 {
762 handleGroupNameAttrPatch(*groupsAttribute, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530763 serverType, dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530764 }
765 if (serviceEnabled)
766 {
767 // if user has given the value as true then enable
768 // the service. if user has given false then no-op
769 // as service is already stopped.
770 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530771 {
Ratan Guptaab828d72019-04-22 14:18:33 +0530772 handleServiceEnablePatch(*serviceEnabled, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530773 serverType, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +0530774 }
Ratan Guptaab828d72019-04-22 14:18:33 +0530775 }
776 else
777 {
778 // if user has not given the service enabled value
779 // then revert it to the same state as it was
780 // before.
781 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530782 serverType, dbusObjectPath);
Ratan Guptaab828d72019-04-22 14:18:33 +0530783 }
784 });
Ratan Gupta8a07d282019-03-16 08:33:47 +0530785 }
786
Ed Tanous1abe55e2018-09-05 08:30:59 -0700787 void doGet(crow::Response& res, const crow::Request& req,
788 const std::vector<std::string>& params) override
789 {
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530790 auto asyncResp = std::make_shared<AsyncResp>(res);
791 res.jsonValue = {
792 {"@odata.context", "/redfish/v1/"
793 "$metadata#AccountService.AccountService"},
794 {"@odata.id", "/redfish/v1/AccountService"},
795 {"@odata.type", "#AccountService."
Marri Devender Rao37cce912019-02-20 01:05:22 -0600796 "v1_4_0.AccountService"},
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530797 {"Id", "AccountService"},
798 {"Name", "Account Service"},
799 {"Description", "Account Service"},
800 {"ServiceEnabled", true},
AppaRao Puli343ff2e2019-03-24 00:42:13 +0530801 {"MaxPasswordLength", 20},
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530802 {"Accounts",
803 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600804 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
805 {"LDAP",
806 {{"Certificates",
807 {{"@odata.id",
808 "/redfish/v1/AccountService/LDAP/Certificates"}}}}}};
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530809 crow::connections::systemBus->async_method_call(
810 [asyncResp](
811 const boost::system::error_code ec,
812 const std::vector<std::pair<
Ed Tanousabf2add2019-01-22 16:40:12 -0800813 std::string, std::variant<uint32_t, uint16_t, uint8_t>>>&
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530814 propertiesList) {
815 if (ec)
816 {
817 messages::internalError(asyncResp->res);
818 return;
819 }
820 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
821 << "properties for AccountService";
822 for (const std::pair<std::string,
Ed Tanousabf2add2019-01-22 16:40:12 -0800823 std::variant<uint32_t, uint16_t, uint8_t>>&
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530824 property : propertiesList)
825 {
826 if (property.first == "MinPasswordLength")
827 {
828 const uint8_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800829 std::get_if<uint8_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530830 if (value != nullptr)
831 {
832 asyncResp->res.jsonValue["MinPasswordLength"] =
833 *value;
834 }
835 }
836 if (property.first == "AccountUnlockTimeout")
837 {
838 const uint32_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800839 std::get_if<uint32_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530840 if (value != nullptr)
841 {
842 asyncResp->res.jsonValue["AccountLockoutDuration"] =
843 *value;
844 }
845 }
846 if (property.first == "MaxLoginAttemptBeforeLockout")
847 {
848 const uint16_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800849 std::get_if<uint16_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530850 if (value != nullptr)
851 {
852 asyncResp->res
853 .jsonValue["AccountLockoutThreshold"] = *value;
854 }
855 }
856 }
857 },
858 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
859 "org.freedesktop.DBus.Properties", "GetAll",
860 "xyz.openbmc_project.User.AccountPolicy");
Ratan Gupta6973a582018-12-13 18:25:44 +0530861
Ratan Guptaab828d72019-04-22 14:18:33 +0530862 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
863 const std::string& ldapType) {
864 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
865 };
866
867 getLDAPConfigData("LDAP", callback);
868 getLDAPConfigData("ActiveDirectory", callback);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530869 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530870
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530871 void doPatch(crow::Response& res, const crow::Request& req,
872 const std::vector<std::string>& params) override
873 {
874 auto asyncResp = std::make_shared<AsyncResp>(res);
875
876 std::optional<uint32_t> unlockTimeout;
877 std::optional<uint16_t> lockoutThreshold;
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530878 std::optional<uint16_t> minPasswordLength;
879 std::optional<uint16_t> maxPasswordLength;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530880 std::optional<nlohmann::json> ldapObject;
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530881 std::optional<nlohmann::json> activeDirectoryObject;
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530882
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530883 if (!json_util::readJson(req, res, "AccountLockoutDuration",
884 unlockTimeout, "AccountLockoutThreshold",
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530885 lockoutThreshold, "MaxPasswordLength",
886 maxPasswordLength, "MinPasswordLength",
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530887 minPasswordLength, "LDAP", ldapObject,
888 "ActiveDirectory", activeDirectoryObject))
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530889 {
890 return;
891 }
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530892
893 if (minPasswordLength)
894 {
895 messages::propertyNotWritable(asyncResp->res, "MinPasswordLength");
896 }
897
898 if (maxPasswordLength)
899 {
900 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
901 }
902
Ratan Gupta8a07d282019-03-16 08:33:47 +0530903 if (ldapObject)
904 {
905 handleLDAPPatch(*ldapObject, asyncResp, req, params, "LDAP");
906 }
907
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530908 if (activeDirectoryObject)
909 {
910 handleLDAPPatch(*activeDirectoryObject, asyncResp, req, params,
911 "ActiveDirectory");
912 }
913
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530914 if (unlockTimeout)
915 {
916 crow::connections::systemBus->async_method_call(
917 [asyncResp](const boost::system::error_code ec) {
918 if (ec)
919 {
920 messages::internalError(asyncResp->res);
921 return;
922 }
Ratan Guptaadd61332019-02-13 20:49:16 +0530923 messages::success(asyncResp->res);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530924 },
925 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
926 "org.freedesktop.DBus.Properties", "Set",
927 "xyz.openbmc_project.User.AccountPolicy",
Ed Tanousabf2add2019-01-22 16:40:12 -0800928 "AccountUnlockTimeout", std::variant<uint32_t>(*unlockTimeout));
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530929 }
930 if (lockoutThreshold)
931 {
932 crow::connections::systemBus->async_method_call(
933 [asyncResp](const boost::system::error_code ec) {
934 if (ec)
935 {
936 messages::internalError(asyncResp->res);
937 return;
938 }
Ratan Guptaadd61332019-02-13 20:49:16 +0530939 messages::success(asyncResp->res);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530940 },
941 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
942 "org.freedesktop.DBus.Properties", "Set",
943 "xyz.openbmc_project.User.AccountPolicy",
944 "MaxLoginAttemptBeforeLockout",
Ed Tanousabf2add2019-01-22 16:40:12 -0800945 std::variant<uint16_t>(*lockoutThreshold));
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530946 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700947 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +0100948};
Tanousf00032d2018-11-05 01:18:10 -0300949
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700950class AccountsCollection : public Node
951{
952 public:
953 AccountsCollection(CrowApp& app) :
954 Node(app, "/redfish/v1/AccountService/Accounts/")
955 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700956 entityPrivileges = {
957 {boost::beast::http::verb::get,
958 {{"ConfigureUsers"}, {"ConfigureManager"}}},
959 {boost::beast::http::verb::head, {{"Login"}}},
960 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
961 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
962 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
963 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
964 }
965
966 private:
967 void doGet(crow::Response& res, const crow::Request& req,
968 const std::vector<std::string>& params) override
969 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700970 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous0f74e642018-11-12 15:17:05 -0800971 res.jsonValue = {{"@odata.context",
972 "/redfish/v1/"
973 "$metadata#ManagerAccountCollection."
974 "ManagerAccountCollection"},
975 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
976 {"@odata.type", "#ManagerAccountCollection."
977 "ManagerAccountCollection"},
978 {"Name", "Accounts Collection"},
979 {"Description", "BMC User Accounts"}};
980
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700981 crow::connections::systemBus->async_method_call(
982 [asyncResp](const boost::system::error_code ec,
983 const ManagedObjectType& users) {
984 if (ec)
985 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700986 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700987 return;
988 }
989
990 nlohmann::json& memberArray =
991 asyncResp->res.jsonValue["Members"];
992 memberArray = nlohmann::json::array();
993
994 asyncResp->res.jsonValue["Members@odata.count"] = users.size();
995 for (auto& user : users)
996 {
997 const std::string& path =
998 static_cast<const std::string&>(user.first);
999 std::size_t lastIndex = path.rfind("/");
1000 if (lastIndex == std::string::npos)
1001 {
1002 lastIndex = 0;
1003 }
1004 else
1005 {
1006 lastIndex += 1;
1007 }
1008 memberArray.push_back(
1009 {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
1010 path.substr(lastIndex)}});
1011 }
1012 },
1013 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1014 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1015 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001016 void doPost(crow::Response& res, const crow::Request& req,
1017 const std::vector<std::string>& params) override
1018 {
1019 auto asyncResp = std::make_shared<AsyncResp>(res);
1020
Ed Tanous9712f8a2018-09-21 13:38:49 -07001021 std::string username;
1022 std::string password;
Ed Tanousa24526d2018-12-10 15:17:59 -08001023 std::optional<std::string> roleId("User");
1024 std::optional<bool> enabled = true;
Ed Tanous9712f8a2018-09-21 13:38:49 -07001025 if (!json_util::readJson(req, res, "UserName", username, "Password",
1026 password, "RoleId", roleId, "Enabled",
1027 enabled))
Ed Tanous04ae99e2018-09-20 15:54:36 -07001028 {
1029 return;
1030 }
1031
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301032 std::string priv = getRoleIdFromPrivilege(*roleId);
1033 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001034 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001035 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001036 return;
1037 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001038 roleId = priv;
Ed Tanous04ae99e2018-09-20 15:54:36 -07001039
1040 crow::connections::systemBus->async_method_call(
Ed Tanous9712f8a2018-09-21 13:38:49 -07001041 [asyncResp, username, password{std::move(password)}](
Ed Tanous04ae99e2018-09-20 15:54:36 -07001042 const boost::system::error_code ec) {
1043 if (ec)
1044 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001045 messages::resourceAlreadyExists(
1046 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
1047 "UserName", username);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001048 return;
1049 }
1050
1051 if (!pamUpdatePassword(username, password))
1052 {
1053 // At this point we have a user that's been created, but the
1054 // password set failed. Something is wrong, so delete the
1055 // user that we've already created
1056 crow::connections::systemBus->async_method_call(
1057 [asyncResp](const boost::system::error_code ec) {
1058 if (ec)
1059 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001060 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001061 return;
1062 }
1063
Jason M. Billsf12894f2018-10-09 12:45:45 -07001064 messages::invalidObject(asyncResp->res, "Password");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001065 },
1066 "xyz.openbmc_project.User.Manager",
1067 "/xyz/openbmc_project/user/" + username,
1068 "xyz.openbmc_project.Object.Delete", "Delete");
1069
1070 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1071 return;
1072 }
1073
Jason M. Billsf12894f2018-10-09 12:45:45 -07001074 messages::created(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001075 asyncResp->res.addHeader(
1076 "Location",
1077 "/redfish/v1/AccountService/Accounts/" + username);
1078 },
1079 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ed Tanous9712f8a2018-09-21 13:38:49 -07001080 "xyz.openbmc_project.User.Manager", "CreateUser", username,
Ed Tanous04ae99e2018-09-20 15:54:36 -07001081 std::array<const char*, 4>{"ipmi", "redfish", "ssh", "web"},
Ed Tanous9712f8a2018-09-21 13:38:49 -07001082 *roleId, *enabled);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001083 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001084};
1085
1086class ManagerAccount : public Node
1087{
1088 public:
1089 ManagerAccount(CrowApp& app) :
1090 Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
1091 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001092 entityPrivileges = {
1093 {boost::beast::http::verb::get,
1094 {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
1095 {boost::beast::http::verb::head, {{"Login"}}},
1096 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
1097 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
1098 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
1099 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
1100 }
1101
1102 private:
1103 void doGet(crow::Response& res, const crow::Request& req,
1104 const std::vector<std::string>& params) override
1105 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001106 res.jsonValue = {
1107 {"@odata.context",
1108 "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
1109 {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
Ed Tanous0f74e642018-11-12 15:17:05 -08001110 {"Name", "User Account"},
1111 {"Description", "User Account"},
1112 {"Password", nullptr},
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301113 {"RoleId", "Administrator"}};
Ed Tanous0f74e642018-11-12 15:17:05 -08001114
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001115 auto asyncResp = std::make_shared<AsyncResp>(res);
1116
1117 if (params.size() != 1)
1118 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001119 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001120 return;
1121 }
1122
1123 crow::connections::systemBus->async_method_call(
1124 [asyncResp, accountName{std::string(params[0])}](
1125 const boost::system::error_code ec,
1126 const ManagedObjectType& users) {
1127 if (ec)
1128 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001129 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001130 return;
1131 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301132 auto userIt = users.begin();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001133
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301134 for (; userIt != users.end(); userIt++)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001135 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301136 if (boost::ends_with(userIt->first.str, "/" + accountName))
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001137 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301138 break;
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001139 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301140 }
1141 if (userIt == users.end())
1142 {
1143 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1144 accountName);
1145 return;
1146 }
1147 for (const auto& interface : userIt->second)
1148 {
1149 if (interface.first ==
1150 "xyz.openbmc_project.User.Attributes")
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001151 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301152 for (const auto& property : interface.second)
Ed Tanous65b0dc32018-09-19 16:04:03 -07001153 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301154 if (property.first == "UserEnabled")
Ed Tanous65b0dc32018-09-19 16:04:03 -07001155 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301156 const bool* userEnabled =
Ed Tanousabf2add2019-01-22 16:40:12 -08001157 std::get_if<bool>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301158 if (userEnabled == nullptr)
Ed Tanous65b0dc32018-09-19 16:04:03 -07001159 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301160 BMCWEB_LOG_ERROR
1161 << "UserEnabled wasn't a bool";
1162 messages::internalError(asyncResp->res);
1163 return;
Ed Tanous65b0dc32018-09-19 16:04:03 -07001164 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301165 asyncResp->res.jsonValue["Enabled"] =
1166 *userEnabled;
1167 }
1168 else if (property.first ==
1169 "UserLockedForFailedAttempt")
1170 {
1171 const bool* userLocked =
Ed Tanousabf2add2019-01-22 16:40:12 -08001172 std::get_if<bool>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301173 if (userLocked == nullptr)
1174 {
1175 BMCWEB_LOG_ERROR << "UserLockedForF"
1176 "ailedAttempt "
1177 "wasn't a bool";
1178 messages::internalError(asyncResp->res);
1179 return;
1180 }
1181 asyncResp->res.jsonValue["Locked"] =
1182 *userLocked;
Ratan Gupta24c85422019-01-30 19:41:24 +05301183 asyncResp->res.jsonValue
1184 ["Locked@Redfish.AllowableValues"] = {
Gunnar Mills4d64ce32019-03-29 16:34:56 -05001185 "false"};
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301186 }
1187 else if (property.first == "UserPrivilege")
1188 {
1189 const std::string* userRolePtr =
Ed Tanousabf2add2019-01-22 16:40:12 -08001190 std::get_if<std::string>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301191 if (userRolePtr == nullptr)
1192 {
1193 BMCWEB_LOG_ERROR
1194 << "UserPrivilege wasn't a "
1195 "string";
1196 messages::internalError(asyncResp->res);
1197 return;
1198 }
1199 std::string priv =
1200 getPrivilegeFromRoleId(*userRolePtr);
1201 if (priv.empty())
1202 {
1203 BMCWEB_LOG_ERROR << "Invalid user role";
1204 messages::internalError(asyncResp->res);
1205 return;
1206 }
1207 asyncResp->res.jsonValue["RoleId"] = priv;
1208
1209 asyncResp->res.jsonValue["Links"]["Role"] = {
1210 {"@odata.id", "/redfish/v1/AccountService/"
1211 "Roles/" +
1212 priv}};
Ed Tanous65b0dc32018-09-19 16:04:03 -07001213 }
1214 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001215 }
1216 }
1217
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301218 asyncResp->res.jsonValue["@odata.id"] =
1219 "/redfish/v1/AccountService/Accounts/" + accountName;
1220 asyncResp->res.jsonValue["Id"] = accountName;
1221 asyncResp->res.jsonValue["UserName"] = accountName;
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001222 },
1223 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1224 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1225 }
Ed Tanousa8408792018-09-05 16:08:38 -07001226
1227 void doPatch(crow::Response& res, const crow::Request& req,
1228 const std::vector<std::string>& params) override
1229 {
1230 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousa8408792018-09-05 16:08:38 -07001231 if (params.size() != 1)
1232 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001233 messages::internalError(asyncResp->res);
Ed Tanousa8408792018-09-05 16:08:38 -07001234 return;
1235 }
1236
Ed Tanousa24526d2018-12-10 15:17:59 -08001237 std::optional<std::string> newUserName;
1238 std::optional<std::string> password;
1239 std::optional<bool> enabled;
1240 std::optional<std::string> roleId;
Ratan Gupta24c85422019-01-30 19:41:24 +05301241 std::optional<bool> locked;
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301242 if (!json_util::readJson(req, res, "UserName", newUserName, "Password",
Ratan Gupta24c85422019-01-30 19:41:24 +05301243 password, "RoleId", roleId, "Enabled", enabled,
1244 "Locked", locked))
Ed Tanousa8408792018-09-05 16:08:38 -07001245 {
1246 return;
1247 }
1248
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301249 const std::string& username = params[0];
Ed Tanousa8408792018-09-05 16:08:38 -07001250
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301251 if (!newUserName)
1252 {
1253 // If the username isn't being updated, we can update the properties
1254 // directly
Ratan Gupta24c85422019-01-30 19:41:24 +05301255 updateUserProperties(asyncResp, username, password, enabled, roleId,
1256 locked);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301257 return;
1258 }
1259 else
1260 {
1261 crow::connections::systemBus->async_method_call(
1262 [this, asyncResp, username, password(std::move(password)),
1263 roleId(std::move(roleId)), enabled(std::move(enabled)),
Ratan Gupta24c85422019-01-30 19:41:24 +05301264 newUser{std::string(*newUserName)}, locked(std::move(locked))](
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301265 const boost::system::error_code ec) {
1266 if (ec)
Ed Tanousa8408792018-09-05 16:08:38 -07001267 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301268 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1269 messages::resourceNotFound(
1270 asyncResp->res,
1271 "#ManagerAccount.v1_0_3.ManagerAccount", username);
1272 return;
1273 }
1274
1275 updateUserProperties(asyncResp, newUser, password, enabled,
Ratan Gupta24c85422019-01-30 19:41:24 +05301276 roleId, locked);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301277 },
1278 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1279 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1280 *newUserName);
1281 }
1282 }
1283
1284 void updateUserProperties(std::shared_ptr<AsyncResp> asyncResp,
1285 const std::string& username,
Ed Tanousa24526d2018-12-10 15:17:59 -08001286 std::optional<std::string> password,
1287 std::optional<bool> enabled,
Ratan Gupta24c85422019-01-30 19:41:24 +05301288 std::optional<std::string> roleId,
1289 std::optional<bool> locked)
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301290 {
1291 if (password)
1292 {
1293 if (!pamUpdatePassword(username, *password))
1294 {
1295 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1296 messages::internalError(asyncResp->res);
1297 return;
1298 }
1299 }
1300
Ratan Gupta24c85422019-01-30 19:41:24 +05301301 std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
1302 dbus::utility::escapePathForDbus(dbusObjectPath);
1303
Ratan Gupta22c33712019-05-03 21:50:28 +05301304 dbus::utility::checkDbusPathExists(
Ratan Gupta24c85422019-01-30 19:41:24 +05301305 dbusObjectPath,
1306 [dbusObjectPath(std::move(dbusObjectPath)), username,
1307 password(std::move(password)), roleId(std::move(roleId)),
1308 enabled(std::move(enabled)), locked(std::move(locked)),
1309 asyncResp{std::move(asyncResp)}](int rc) {
1310 if (!rc)
1311 {
1312 messages::invalidObject(asyncResp->res, username.c_str());
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301313 return;
Ratan Gupta24c85422019-01-30 19:41:24 +05301314 }
1315 if (enabled)
1316 {
1317 crow::connections::systemBus->async_method_call(
1318 [asyncResp](const boost::system::error_code ec) {
1319 if (ec)
1320 {
1321 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1322 << ec;
1323 messages::internalError(asyncResp->res);
1324 return;
1325 }
1326 messages::success(asyncResp->res);
1327 return;
1328 },
1329 "xyz.openbmc_project.User.Manager",
1330 dbusObjectPath.c_str(),
1331 "org.freedesktop.DBus.Properties", "Set",
1332 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1333 std::variant<bool>{*enabled});
1334 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001335
Ratan Gupta24c85422019-01-30 19:41:24 +05301336 if (roleId)
1337 {
1338 std::string priv = getRoleIdFromPrivilege(*roleId);
1339 if (priv.empty())
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301340 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301341 messages::propertyValueNotInList(asyncResp->res,
1342 *roleId, "RoleId");
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301343 return;
1344 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301345
1346 crow::connections::systemBus->async_method_call(
1347 [asyncResp](const boost::system::error_code ec) {
1348 if (ec)
1349 {
1350 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1351 << ec;
1352 messages::internalError(asyncResp->res);
1353 return;
1354 }
1355 messages::success(asyncResp->res);
1356 },
1357 "xyz.openbmc_project.User.Manager",
1358 dbusObjectPath.c_str(),
1359 "org.freedesktop.DBus.Properties", "Set",
1360 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1361 std::variant<std::string>{priv});
1362 }
1363
1364 if (locked)
1365 {
1366 // admin can unlock the account which is locked by
1367 // successive authentication failures but admin should not
1368 // be allowed to lock an account.
1369 if (*locked)
1370 {
1371 messages::propertyValueNotInList(asyncResp->res, "true",
1372 "Locked");
1373 return;
1374 }
1375
1376 crow::connections::systemBus->async_method_call(
1377 [asyncResp](const boost::system::error_code ec) {
1378 if (ec)
1379 {
1380 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1381 << ec;
1382 messages::internalError(asyncResp->res);
1383 return;
1384 }
1385 messages::success(asyncResp->res);
1386 return;
1387 },
1388 "xyz.openbmc_project.User.Manager",
1389 dbusObjectPath.c_str(),
1390 "org.freedesktop.DBus.Properties", "Set",
1391 "xyz.openbmc_project.User.Attributes",
1392 "UserLockedForFailedAttempt",
1393 sdbusplus::message::variant<bool>{*locked});
1394 }
1395 });
Ed Tanousa8408792018-09-05 16:08:38 -07001396 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001397
1398 void doDelete(crow::Response& res, const crow::Request& req,
1399 const std::vector<std::string>& params) override
1400 {
1401 auto asyncResp = std::make_shared<AsyncResp>(res);
1402
1403 if (params.size() != 1)
1404 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001405 messages::internalError(asyncResp->res);
Ed Tanous06e086d2018-09-19 17:19:52 -07001406 return;
1407 }
1408
1409 const std::string userPath = "/xyz/openbmc_project/user/" + params[0];
1410
1411 crow::connections::systemBus->async_method_call(
1412 [asyncResp, username{std::move(params[0])}](
1413 const boost::system::error_code ec) {
1414 if (ec)
1415 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001416 messages::resourceNotFound(
1417 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
1418 username);
Ed Tanous06e086d2018-09-19 17:19:52 -07001419 return;
1420 }
1421
Jason M. Billsf12894f2018-10-09 12:45:45 -07001422 messages::accountRemoved(asyncResp->res);
Ed Tanous06e086d2018-09-19 17:19:52 -07001423 },
1424 "xyz.openbmc_project.User.Manager", userPath,
1425 "xyz.openbmc_project.Object.Delete", "Delete");
1426 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301427};
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001428
Ed Tanous1abe55e2018-09-05 08:30:59 -07001429} // namespace redfish