blob: 8d03a2542fc651e91d22b7f5079b63afef381a86 [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 {
639 std::optional<nlohmann::json> authentication;
640 std::optional<nlohmann::json> ldapService;
641 std::optional<std::string> accountProviderType;
642 std::optional<std::vector<std::string>> serviceAddressList;
643 std::optional<bool> serviceEnabled;
644 std::optional<std::vector<std::string>> baseDNList;
645 std::optional<std::string> userNameAttribute;
646 std::optional<std::string> groupsAttribute;
647 std::optional<std::string> userName;
648 std::optional<std::string> password;
649
650 if (!json_util::readJson(input, asyncResp->res, "Authentication",
651 authentication, "LDAPService", ldapService,
652 "ServiceAddresses", serviceAddressList,
653 "AccountProviderType", accountProviderType,
654 "ServiceEnabled", serviceEnabled))
655 {
656 return;
657 }
658
659 if (authentication)
660 {
661 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
662 password);
663 }
664 if (ldapService)
665 {
666 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
667 userNameAttribute, groupsAttribute);
668 }
669 if (accountProviderType)
670 {
671 messages::propertyNotWritable(asyncResp->res,
672 "AccountProviderType");
673 }
674 if (serviceAddressList)
675 {
676 if ((*serviceAddressList).size() == 0)
677 {
678 messages::propertyValueNotInList(asyncResp->res, "[]",
679 "ServiceAddress");
680 return;
681 }
682 }
683 if (baseDNList)
684 {
685 if ((*baseDNList).size() == 0)
686 {
687 messages::propertyValueNotInList(asyncResp->res, "[]",
688 "BaseDistinguishedNames");
689 return;
690 }
691 }
692
693 // nothing to update, then return
694 if (!userName && !password && !serviceAddressList && !baseDNList &&
695 !userNameAttribute && !groupsAttribute && !serviceEnabled)
696 {
697 return;
698 }
699
700 // Get the existing resource first then keep modifying
701 // whenever any property gets updated.
Ratan Guptaab828d72019-04-22 14:18:33 +0530702 getLDAPConfigData(serverType, [this, asyncResp, userName, password,
703 baseDNList, userNameAttribute,
704 groupsAttribute, accountProviderType,
705 serviceAddressList, serviceEnabled](
706 bool success, LDAPConfigData confData,
707 const std::string& serverType) {
708 if (!success)
709 {
710 messages::internalError(asyncResp->res);
711 return;
712 }
713 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverType);
714 if (confData.serviceEnabled)
715 {
716 // Disable the service first and update the rest of
717 // the properties.
718 handleServiceEnablePatch(false, asyncResp, serverType,
719 ldapConfigObject);
720 }
Ratan Gupta8a07d282019-03-16 08:33:47 +0530721
Ratan Guptaab828d72019-04-22 14:18:33 +0530722 if (serviceAddressList)
723 {
724 handleServiceAddressPatch(*serviceAddressList, asyncResp,
725 serverType, ldapConfigObject);
726 }
727 if (userName)
728 {
729 handleUserNamePatch(*userName, asyncResp, serverType,
730 ldapConfigObject);
731 }
732 if (password)
733 {
734 handlePasswordPatch(*password, asyncResp, serverType,
735 ldapConfigObject);
736 }
Ratan Gupta8a07d282019-03-16 08:33:47 +0530737
Ratan Guptaab828d72019-04-22 14:18:33 +0530738 if (baseDNList)
739 {
740 handleBaseDNPatch(*baseDNList, asyncResp, serverType,
741 ldapConfigObject);
742 }
743 if (userNameAttribute)
744 {
745 handleUserNameAttrPatch(*userNameAttribute, asyncResp,
746 serverType, ldapConfigObject);
747 }
748 if (groupsAttribute)
749 {
750 handleGroupNameAttrPatch(*groupsAttribute, asyncResp,
751 serverType, ldapConfigObject);
752 }
753 if (serviceEnabled)
754 {
755 // if user has given the value as true then enable
756 // the service. if user has given false then no-op
757 // as service is already stopped.
758 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530759 {
Ratan Guptaab828d72019-04-22 14:18:33 +0530760 handleServiceEnablePatch(*serviceEnabled, asyncResp,
Ratan Gupta8a07d282019-03-16 08:33:47 +0530761 serverType, ldapConfigObject);
762 }
Ratan Guptaab828d72019-04-22 14:18:33 +0530763 }
764 else
765 {
766 // if user has not given the service enabled value
767 // then revert it to the same state as it was
768 // before.
769 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
770 serverType, ldapConfigObject);
771 }
772 });
Ratan Gupta8a07d282019-03-16 08:33:47 +0530773 }
774
Ed Tanous1abe55e2018-09-05 08:30:59 -0700775 void doGet(crow::Response& res, const crow::Request& req,
776 const std::vector<std::string>& params) override
777 {
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530778 auto asyncResp = std::make_shared<AsyncResp>(res);
779 res.jsonValue = {
780 {"@odata.context", "/redfish/v1/"
781 "$metadata#AccountService.AccountService"},
782 {"@odata.id", "/redfish/v1/AccountService"},
783 {"@odata.type", "#AccountService."
Ratan Gupta6973a582018-12-13 18:25:44 +0530784 "v1_3_1.AccountService"},
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530785 {"Id", "AccountService"},
786 {"Name", "Account Service"},
787 {"Description", "Account Service"},
788 {"ServiceEnabled", true},
AppaRao Puli343ff2e2019-03-24 00:42:13 +0530789 {"MaxPasswordLength", 20},
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530790 {"Accounts",
791 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
792 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}}};
Ed Tanous0f74e642018-11-12 15:17:05 -0800793
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530794 crow::connections::systemBus->async_method_call(
795 [asyncResp](
796 const boost::system::error_code ec,
797 const std::vector<std::pair<
Ed Tanousabf2add2019-01-22 16:40:12 -0800798 std::string, std::variant<uint32_t, uint16_t, uint8_t>>>&
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530799 propertiesList) {
800 if (ec)
801 {
802 messages::internalError(asyncResp->res);
803 return;
804 }
805 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
806 << "properties for AccountService";
807 for (const std::pair<std::string,
Ed Tanousabf2add2019-01-22 16:40:12 -0800808 std::variant<uint32_t, uint16_t, uint8_t>>&
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530809 property : propertiesList)
810 {
811 if (property.first == "MinPasswordLength")
812 {
813 const uint8_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800814 std::get_if<uint8_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530815 if (value != nullptr)
816 {
817 asyncResp->res.jsonValue["MinPasswordLength"] =
818 *value;
819 }
820 }
821 if (property.first == "AccountUnlockTimeout")
822 {
823 const uint32_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800824 std::get_if<uint32_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530825 if (value != nullptr)
826 {
827 asyncResp->res.jsonValue["AccountLockoutDuration"] =
828 *value;
829 }
830 }
831 if (property.first == "MaxLoginAttemptBeforeLockout")
832 {
833 const uint16_t* value =
Ed Tanousabf2add2019-01-22 16:40:12 -0800834 std::get_if<uint16_t>(&property.second);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530835 if (value != nullptr)
836 {
837 asyncResp->res
838 .jsonValue["AccountLockoutThreshold"] = *value;
839 }
840 }
841 }
842 },
843 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
844 "org.freedesktop.DBus.Properties", "GetAll",
845 "xyz.openbmc_project.User.AccountPolicy");
Ratan Gupta6973a582018-12-13 18:25:44 +0530846
Ratan Guptaab828d72019-04-22 14:18:33 +0530847 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
848 const std::string& ldapType) {
849 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
850 };
851
852 getLDAPConfigData("LDAP", callback);
853 getLDAPConfigData("ActiveDirectory", callback);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530854 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530855
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530856 void doPatch(crow::Response& res, const crow::Request& req,
857 const std::vector<std::string>& params) override
858 {
859 auto asyncResp = std::make_shared<AsyncResp>(res);
860
861 std::optional<uint32_t> unlockTimeout;
862 std::optional<uint16_t> lockoutThreshold;
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530863 std::optional<uint16_t> minPasswordLength;
864 std::optional<uint16_t> maxPasswordLength;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530865 std::optional<nlohmann::json> ldapObject;
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530866
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530867 if (!json_util::readJson(req, res, "AccountLockoutDuration",
868 unlockTimeout, "AccountLockoutThreshold",
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530869 lockoutThreshold, "MaxPasswordLength",
870 maxPasswordLength, "MinPasswordLength",
871 minPasswordLength))
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530872 {
873 return;
874 }
Ratan Gupta19fb6e72019-03-04 13:30:50 +0530875
876 if (minPasswordLength)
877 {
878 messages::propertyNotWritable(asyncResp->res, "MinPasswordLength");
879 }
880
881 if (maxPasswordLength)
882 {
883 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
884 }
885
Ratan Gupta8a07d282019-03-16 08:33:47 +0530886 if (ldapObject)
887 {
888 handleLDAPPatch(*ldapObject, asyncResp, req, params, "LDAP");
889 }
890
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530891 if (unlockTimeout)
892 {
893 crow::connections::systemBus->async_method_call(
894 [asyncResp](const boost::system::error_code ec) {
895 if (ec)
896 {
897 messages::internalError(asyncResp->res);
898 return;
899 }
Ratan Guptaadd61332019-02-13 20:49:16 +0530900 messages::success(asyncResp->res);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530901 },
902 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
903 "org.freedesktop.DBus.Properties", "Set",
904 "xyz.openbmc_project.User.AccountPolicy",
Ed Tanousabf2add2019-01-22 16:40:12 -0800905 "AccountUnlockTimeout", std::variant<uint32_t>(*unlockTimeout));
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530906 }
907 if (lockoutThreshold)
908 {
909 crow::connections::systemBus->async_method_call(
910 [asyncResp](const boost::system::error_code ec) {
911 if (ec)
912 {
913 messages::internalError(asyncResp->res);
914 return;
915 }
Ratan Guptaadd61332019-02-13 20:49:16 +0530916 messages::success(asyncResp->res);
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530917 },
918 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
919 "org.freedesktop.DBus.Properties", "Set",
920 "xyz.openbmc_project.User.AccountPolicy",
921 "MaxLoginAttemptBeforeLockout",
Ed Tanousabf2add2019-01-22 16:40:12 -0800922 std::variant<uint16_t>(*lockoutThreshold));
AppaRao Puli3d958bb2018-12-25 12:45:54 +0530923 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700924 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +0100925};
Tanousf00032d2018-11-05 01:18:10 -0300926
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700927class AccountsCollection : public Node
928{
929 public:
930 AccountsCollection(CrowApp& app) :
931 Node(app, "/redfish/v1/AccountService/Accounts/")
932 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700933 entityPrivileges = {
934 {boost::beast::http::verb::get,
935 {{"ConfigureUsers"}, {"ConfigureManager"}}},
936 {boost::beast::http::verb::head, {{"Login"}}},
937 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
938 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
939 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
940 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
941 }
942
943 private:
944 void doGet(crow::Response& res, const crow::Request& req,
945 const std::vector<std::string>& params) override
946 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700947 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous0f74e642018-11-12 15:17:05 -0800948 res.jsonValue = {{"@odata.context",
949 "/redfish/v1/"
950 "$metadata#ManagerAccountCollection."
951 "ManagerAccountCollection"},
952 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
953 {"@odata.type", "#ManagerAccountCollection."
954 "ManagerAccountCollection"},
955 {"Name", "Accounts Collection"},
956 {"Description", "BMC User Accounts"}};
957
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700958 crow::connections::systemBus->async_method_call(
959 [asyncResp](const boost::system::error_code ec,
960 const ManagedObjectType& users) {
961 if (ec)
962 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700963 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700964 return;
965 }
966
967 nlohmann::json& memberArray =
968 asyncResp->res.jsonValue["Members"];
969 memberArray = nlohmann::json::array();
970
971 asyncResp->res.jsonValue["Members@odata.count"] = users.size();
972 for (auto& user : users)
973 {
974 const std::string& path =
975 static_cast<const std::string&>(user.first);
976 std::size_t lastIndex = path.rfind("/");
977 if (lastIndex == std::string::npos)
978 {
979 lastIndex = 0;
980 }
981 else
982 {
983 lastIndex += 1;
984 }
985 memberArray.push_back(
986 {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
987 path.substr(lastIndex)}});
988 }
989 },
990 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
991 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
992 }
Ed Tanous04ae99e2018-09-20 15:54:36 -0700993 void doPost(crow::Response& res, const crow::Request& req,
994 const std::vector<std::string>& params) override
995 {
996 auto asyncResp = std::make_shared<AsyncResp>(res);
997
Ed Tanous9712f8a2018-09-21 13:38:49 -0700998 std::string username;
999 std::string password;
Ed Tanousa24526d2018-12-10 15:17:59 -08001000 std::optional<std::string> roleId("User");
1001 std::optional<bool> enabled = true;
Ed Tanous9712f8a2018-09-21 13:38:49 -07001002 if (!json_util::readJson(req, res, "UserName", username, "Password",
1003 password, "RoleId", roleId, "Enabled",
1004 enabled))
Ed Tanous04ae99e2018-09-20 15:54:36 -07001005 {
1006 return;
1007 }
1008
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301009 std::string priv = getRoleIdFromPrivilege(*roleId);
1010 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001011 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001012 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001013 return;
1014 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001015 roleId = priv;
Ed Tanous04ae99e2018-09-20 15:54:36 -07001016
1017 crow::connections::systemBus->async_method_call(
Ed Tanous9712f8a2018-09-21 13:38:49 -07001018 [asyncResp, username, password{std::move(password)}](
Ed Tanous04ae99e2018-09-20 15:54:36 -07001019 const boost::system::error_code ec) {
1020 if (ec)
1021 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001022 messages::resourceAlreadyExists(
1023 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
1024 "UserName", username);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001025 return;
1026 }
1027
1028 if (!pamUpdatePassword(username, password))
1029 {
1030 // At this point we have a user that's been created, but the
1031 // password set failed. Something is wrong, so delete the
1032 // user that we've already created
1033 crow::connections::systemBus->async_method_call(
1034 [asyncResp](const boost::system::error_code ec) {
1035 if (ec)
1036 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001037 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001038 return;
1039 }
1040
Jason M. Billsf12894f2018-10-09 12:45:45 -07001041 messages::invalidObject(asyncResp->res, "Password");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001042 },
1043 "xyz.openbmc_project.User.Manager",
1044 "/xyz/openbmc_project/user/" + username,
1045 "xyz.openbmc_project.Object.Delete", "Delete");
1046
1047 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1048 return;
1049 }
1050
Jason M. Billsf12894f2018-10-09 12:45:45 -07001051 messages::created(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001052 asyncResp->res.addHeader(
1053 "Location",
1054 "/redfish/v1/AccountService/Accounts/" + username);
1055 },
1056 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ed Tanous9712f8a2018-09-21 13:38:49 -07001057 "xyz.openbmc_project.User.Manager", "CreateUser", username,
Ed Tanous04ae99e2018-09-20 15:54:36 -07001058 std::array<const char*, 4>{"ipmi", "redfish", "ssh", "web"},
Ed Tanous9712f8a2018-09-21 13:38:49 -07001059 *roleId, *enabled);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001060 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001061};
1062
1063class ManagerAccount : public Node
1064{
1065 public:
1066 ManagerAccount(CrowApp& app) :
1067 Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
1068 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001069 entityPrivileges = {
1070 {boost::beast::http::verb::get,
1071 {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
1072 {boost::beast::http::verb::head, {{"Login"}}},
1073 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
1074 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
1075 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
1076 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
1077 }
1078
1079 private:
1080 void doGet(crow::Response& res, const crow::Request& req,
1081 const std::vector<std::string>& params) override
1082 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001083 res.jsonValue = {
1084 {"@odata.context",
1085 "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
1086 {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
Ed Tanous0f74e642018-11-12 15:17:05 -08001087 {"Name", "User Account"},
1088 {"Description", "User Account"},
1089 {"Password", nullptr},
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301090 {"RoleId", "Administrator"}};
Ed Tanous0f74e642018-11-12 15:17:05 -08001091
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001092 auto asyncResp = std::make_shared<AsyncResp>(res);
1093
1094 if (params.size() != 1)
1095 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001096 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001097 return;
1098 }
1099
1100 crow::connections::systemBus->async_method_call(
1101 [asyncResp, accountName{std::string(params[0])}](
1102 const boost::system::error_code ec,
1103 const ManagedObjectType& users) {
1104 if (ec)
1105 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001106 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001107 return;
1108 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301109 auto userIt = users.begin();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001110
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301111 for (; userIt != users.end(); userIt++)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001112 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301113 if (boost::ends_with(userIt->first.str, "/" + accountName))
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001114 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301115 break;
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001116 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301117 }
1118 if (userIt == users.end())
1119 {
1120 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1121 accountName);
1122 return;
1123 }
1124 for (const auto& interface : userIt->second)
1125 {
1126 if (interface.first ==
1127 "xyz.openbmc_project.User.Attributes")
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001128 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301129 for (const auto& property : interface.second)
Ed Tanous65b0dc32018-09-19 16:04:03 -07001130 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301131 if (property.first == "UserEnabled")
Ed Tanous65b0dc32018-09-19 16:04:03 -07001132 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301133 const bool* userEnabled =
Ed Tanousabf2add2019-01-22 16:40:12 -08001134 std::get_if<bool>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301135 if (userEnabled == nullptr)
Ed Tanous65b0dc32018-09-19 16:04:03 -07001136 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301137 BMCWEB_LOG_ERROR
1138 << "UserEnabled wasn't a bool";
1139 messages::internalError(asyncResp->res);
1140 return;
Ed Tanous65b0dc32018-09-19 16:04:03 -07001141 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301142 asyncResp->res.jsonValue["Enabled"] =
1143 *userEnabled;
1144 }
1145 else if (property.first ==
1146 "UserLockedForFailedAttempt")
1147 {
1148 const bool* userLocked =
Ed Tanousabf2add2019-01-22 16:40:12 -08001149 std::get_if<bool>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301150 if (userLocked == nullptr)
1151 {
1152 BMCWEB_LOG_ERROR << "UserLockedForF"
1153 "ailedAttempt "
1154 "wasn't a bool";
1155 messages::internalError(asyncResp->res);
1156 return;
1157 }
1158 asyncResp->res.jsonValue["Locked"] =
1159 *userLocked;
Ratan Gupta24c85422019-01-30 19:41:24 +05301160 asyncResp->res.jsonValue
1161 ["Locked@Redfish.AllowableValues"] = {
Gunnar Mills4d64ce32019-03-29 16:34:56 -05001162 "false"};
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301163 }
1164 else if (property.first == "UserPrivilege")
1165 {
1166 const std::string* userRolePtr =
Ed Tanousabf2add2019-01-22 16:40:12 -08001167 std::get_if<std::string>(&property.second);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301168 if (userRolePtr == nullptr)
1169 {
1170 BMCWEB_LOG_ERROR
1171 << "UserPrivilege wasn't a "
1172 "string";
1173 messages::internalError(asyncResp->res);
1174 return;
1175 }
1176 std::string priv =
1177 getPrivilegeFromRoleId(*userRolePtr);
1178 if (priv.empty())
1179 {
1180 BMCWEB_LOG_ERROR << "Invalid user role";
1181 messages::internalError(asyncResp->res);
1182 return;
1183 }
1184 asyncResp->res.jsonValue["RoleId"] = priv;
1185
1186 asyncResp->res.jsonValue["Links"]["Role"] = {
1187 {"@odata.id", "/redfish/v1/AccountService/"
1188 "Roles/" +
1189 priv}};
Ed Tanous65b0dc32018-09-19 16:04:03 -07001190 }
1191 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001192 }
1193 }
1194
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301195 asyncResp->res.jsonValue["@odata.id"] =
1196 "/redfish/v1/AccountService/Accounts/" + accountName;
1197 asyncResp->res.jsonValue["Id"] = accountName;
1198 asyncResp->res.jsonValue["UserName"] = accountName;
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001199 },
1200 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1201 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1202 }
Ed Tanousa8408792018-09-05 16:08:38 -07001203
1204 void doPatch(crow::Response& res, const crow::Request& req,
1205 const std::vector<std::string>& params) override
1206 {
1207 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousa8408792018-09-05 16:08:38 -07001208 if (params.size() != 1)
1209 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001210 messages::internalError(asyncResp->res);
Ed Tanousa8408792018-09-05 16:08:38 -07001211 return;
1212 }
1213
Ed Tanousa24526d2018-12-10 15:17:59 -08001214 std::optional<std::string> newUserName;
1215 std::optional<std::string> password;
1216 std::optional<bool> enabled;
1217 std::optional<std::string> roleId;
Ratan Gupta24c85422019-01-30 19:41:24 +05301218 std::optional<bool> locked;
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301219 if (!json_util::readJson(req, res, "UserName", newUserName, "Password",
Ratan Gupta24c85422019-01-30 19:41:24 +05301220 password, "RoleId", roleId, "Enabled", enabled,
1221 "Locked", locked))
Ed Tanousa8408792018-09-05 16:08:38 -07001222 {
1223 return;
1224 }
1225
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301226 const std::string& username = params[0];
Ed Tanousa8408792018-09-05 16:08:38 -07001227
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301228 if (!newUserName)
1229 {
1230 // If the username isn't being updated, we can update the properties
1231 // directly
Ratan Gupta24c85422019-01-30 19:41:24 +05301232 updateUserProperties(asyncResp, username, password, enabled, roleId,
1233 locked);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301234 return;
1235 }
1236 else
1237 {
1238 crow::connections::systemBus->async_method_call(
1239 [this, asyncResp, username, password(std::move(password)),
1240 roleId(std::move(roleId)), enabled(std::move(enabled)),
Ratan Gupta24c85422019-01-30 19:41:24 +05301241 newUser{std::string(*newUserName)}, locked(std::move(locked))](
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301242 const boost::system::error_code ec) {
1243 if (ec)
Ed Tanousa8408792018-09-05 16:08:38 -07001244 {
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301245 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1246 messages::resourceNotFound(
1247 asyncResp->res,
1248 "#ManagerAccount.v1_0_3.ManagerAccount", username);
1249 return;
1250 }
1251
1252 updateUserProperties(asyncResp, newUser, password, enabled,
Ratan Gupta24c85422019-01-30 19:41:24 +05301253 roleId, locked);
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301254 },
1255 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1256 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1257 *newUserName);
1258 }
1259 }
1260
1261 void updateUserProperties(std::shared_ptr<AsyncResp> asyncResp,
1262 const std::string& username,
Ed Tanousa24526d2018-12-10 15:17:59 -08001263 std::optional<std::string> password,
1264 std::optional<bool> enabled,
Ratan Gupta24c85422019-01-30 19:41:24 +05301265 std::optional<std::string> roleId,
1266 std::optional<bool> locked)
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301267 {
1268 if (password)
1269 {
1270 if (!pamUpdatePassword(username, *password))
1271 {
1272 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1273 messages::internalError(asyncResp->res);
1274 return;
1275 }
1276 }
1277
Ratan Gupta24c85422019-01-30 19:41:24 +05301278 std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
1279 dbus::utility::escapePathForDbus(dbusObjectPath);
1280
Ratan Gupta22c33712019-05-03 21:50:28 +05301281 dbus::utility::checkDbusPathExists(
Ratan Gupta24c85422019-01-30 19:41:24 +05301282 dbusObjectPath,
1283 [dbusObjectPath(std::move(dbusObjectPath)), username,
1284 password(std::move(password)), roleId(std::move(roleId)),
1285 enabled(std::move(enabled)), locked(std::move(locked)),
1286 asyncResp{std::move(asyncResp)}](int rc) {
1287 if (!rc)
1288 {
1289 messages::invalidObject(asyncResp->res, username.c_str());
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301290 return;
Ratan Gupta24c85422019-01-30 19:41:24 +05301291 }
1292 if (enabled)
1293 {
1294 crow::connections::systemBus->async_method_call(
1295 [asyncResp](const boost::system::error_code ec) {
1296 if (ec)
1297 {
1298 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1299 << ec;
1300 messages::internalError(asyncResp->res);
1301 return;
1302 }
1303 messages::success(asyncResp->res);
1304 return;
1305 },
1306 "xyz.openbmc_project.User.Manager",
1307 dbusObjectPath.c_str(),
1308 "org.freedesktop.DBus.Properties", "Set",
1309 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1310 std::variant<bool>{*enabled});
1311 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001312
Ratan Gupta24c85422019-01-30 19:41:24 +05301313 if (roleId)
1314 {
1315 std::string priv = getRoleIdFromPrivilege(*roleId);
1316 if (priv.empty())
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301317 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301318 messages::propertyValueNotInList(asyncResp->res,
1319 *roleId, "RoleId");
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301320 return;
1321 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301322
1323 crow::connections::systemBus->async_method_call(
1324 [asyncResp](const boost::system::error_code ec) {
1325 if (ec)
1326 {
1327 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1328 << ec;
1329 messages::internalError(asyncResp->res);
1330 return;
1331 }
1332 messages::success(asyncResp->res);
1333 },
1334 "xyz.openbmc_project.User.Manager",
1335 dbusObjectPath.c_str(),
1336 "org.freedesktop.DBus.Properties", "Set",
1337 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1338 std::variant<std::string>{priv});
1339 }
1340
1341 if (locked)
1342 {
1343 // admin can unlock the account which is locked by
1344 // successive authentication failures but admin should not
1345 // be allowed to lock an account.
1346 if (*locked)
1347 {
1348 messages::propertyValueNotInList(asyncResp->res, "true",
1349 "Locked");
1350 return;
1351 }
1352
1353 crow::connections::systemBus->async_method_call(
1354 [asyncResp](const boost::system::error_code ec) {
1355 if (ec)
1356 {
1357 BMCWEB_LOG_ERROR << "D-Bus responses error: "
1358 << ec;
1359 messages::internalError(asyncResp->res);
1360 return;
1361 }
1362 messages::success(asyncResp->res);
1363 return;
1364 },
1365 "xyz.openbmc_project.User.Manager",
1366 dbusObjectPath.c_str(),
1367 "org.freedesktop.DBus.Properties", "Set",
1368 "xyz.openbmc_project.User.Attributes",
1369 "UserLockedForFailedAttempt",
1370 sdbusplus::message::variant<bool>{*locked});
1371 }
1372 });
Ed Tanousa8408792018-09-05 16:08:38 -07001373 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001374
1375 void doDelete(crow::Response& res, const crow::Request& req,
1376 const std::vector<std::string>& params) override
1377 {
1378 auto asyncResp = std::make_shared<AsyncResp>(res);
1379
1380 if (params.size() != 1)
1381 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001382 messages::internalError(asyncResp->res);
Ed Tanous06e086d2018-09-19 17:19:52 -07001383 return;
1384 }
1385
1386 const std::string userPath = "/xyz/openbmc_project/user/" + params[0];
1387
1388 crow::connections::systemBus->async_method_call(
1389 [asyncResp, username{std::move(params[0])}](
1390 const boost::system::error_code ec) {
1391 if (ec)
1392 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001393 messages::resourceNotFound(
1394 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
1395 username);
Ed Tanous06e086d2018-09-19 17:19:52 -07001396 return;
1397 }
1398
Jason M. Billsf12894f2018-10-09 12:45:45 -07001399 messages::accountRemoved(asyncResp->res);
Ed Tanous06e086d2018-09-19 17:19:52 -07001400 },
1401 "xyz.openbmc_project.User.Manager", userPath,
1402 "xyz.openbmc_project.Object.Delete", "Delete");
1403 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +05301404};
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001405
Ed Tanous1abe55e2018-09-05 08:30:59 -07001406} // namespace redfish