blob: 8890ae18de905771a1c17e71afe526233dc5092a [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
John Edward Broadbent7e860f12021-04-08 15:57:16 -070018#include <app.hpp>
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 Tanous52cc1122020-07-18 13:51:21 -070022#include <persistent_data.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070023#include <registries/privilege_registry.hpp>
Ed Tanousa8408792018-09-05 16:08:38 -070024#include <utils/json_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050025
Ed Tanousabf2add2019-01-22 16:40:12 -080026#include <variant>
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace redfish
29{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010030
Ed Tanous23a21a12020-07-25 04:45:05 +000031constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053032 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070033constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053034 "/xyz/openbmc_project/user/ldap/active_directory";
35
Ratan Gupta6973a582018-12-13 18:25:44 +053036constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
37constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
38constexpr const char* ldapConfigInterface =
39 "xyz.openbmc_project.User.Ldap.Config";
40constexpr const char* ldapCreateInterface =
41 "xyz.openbmc_project.User.Ldap.Create";
42constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053043constexpr const char* ldapPrivMapperInterface =
44 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053045constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
46constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
47constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
48constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
49constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
50
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060051struct LDAPRoleMapData
52{
53 std::string groupName;
54 std::string privilege;
55};
56
Ratan Gupta6973a582018-12-13 18:25:44 +053057struct LDAPConfigData
58{
59 std::string uri{};
60 std::string bindDN{};
61 std::string baseDN{};
62 std::string searchScope{};
63 std::string serverType{};
64 bool serviceEnabled = false;
65 std::string userNameAttribute{};
66 std::string groupAttribute{};
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060067 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053068};
69
Patrick Williams19bd78d2020-05-13 17:38:24 -050070using DbusVariantType = std::variant<bool, int32_t, std::string>;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020071
72using DbusInterfaceType = boost::container::flat_map<
73 std::string, boost::container::flat_map<std::string, DbusVariantType>>;
74
75using ManagedObjectType =
76 std::vector<std::pair<sdbusplus::message::object_path, DbusInterfaceType>>;
77
Ratan Gupta6973a582018-12-13 18:25:44 +053078using GetObjectType =
79 std::vector<std::pair<std::string, std::vector<std::string>>>;
AppaRao Puli84e12cb2018-10-11 01:28:15 +053080
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060081inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053082{
83 if (role == "priv-admin")
84 {
85 return "Administrator";
86 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070087 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053088 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053089 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053090 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070091 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053092 {
93 return "Operator";
94 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070095 if ((role == "") || (role == "priv-noaccess"))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +000096 {
97 return "NoAccess";
98 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +053099 return "";
100}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600101inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530102{
103 if (role == "Administrator")
104 {
105 return "priv-admin";
106 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700107 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530108 {
109 return "priv-user";
110 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700111 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530112 {
113 return "priv-operator";
114 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700115 if ((role == "NoAccess") || (role == ""))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +0000116 {
117 return "priv-noaccess";
118 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530119 return "";
120}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700121
zhanghch058d1b46d2021-04-01 11:18:24 +0800122inline void userErrorMessageHandler(
123 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
124 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000125{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000126 if (e == nullptr)
127 {
128 messages::internalError(asyncResp->res);
129 return;
130 }
131
Manojkiran Eda055806b2020-11-03 09:36:28 +0530132 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000133 if (strcmp(errorMessage,
134 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
135 {
136 messages::resourceAlreadyExists(asyncResp->res,
Gunnar Mills8114bd42020-06-11 20:55:21 -0500137 "#ManagerAccount.v1_4_0.ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000138 "UserName", newUser);
139 }
140 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
141 "UserNameDoesNotExist") == 0)
142 {
143 messages::resourceNotFound(
Gunnar Mills8114bd42020-06-11 20:55:21 -0500144 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000145 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700146 else if ((strcmp(errorMessage,
147 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
148 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800149 (strcmp(
150 errorMessage,
151 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
152 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000153 {
154 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
155 }
156 else if (strcmp(errorMessage,
157 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
158 {
159 messages::createLimitReachedForResource(asyncResp->res);
160 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000161 else
162 {
163 messages::internalError(asyncResp->res);
164 }
165
166 return;
167}
168
Ed Tanous81ce6092020-12-17 16:54:55 +0000169inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000170 const LDAPConfigData& confData,
171 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530172{
Ratan Guptaab828d72019-04-22 14:18:33 +0530173 std::string service =
174 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600175 nlohmann::json ldap = {
Ratan Gupta6973a582018-12-13 18:25:44 +0530176 {"ServiceEnabled", confData.serviceEnabled},
177 {"ServiceAddresses", nlohmann::json::array({confData.uri})},
178 {"Authentication",
179 {{"AuthenticationType", "UsernameAndPassword"},
Ratan Gupta6973a582018-12-13 18:25:44 +0530180 {"Username", confData.bindDN},
181 {"Password", nullptr}}},
182 {"LDAPService",
183 {{"SearchSettings",
184 {{"BaseDistinguishedNames",
185 nlohmann::json::array({confData.baseDN})},
186 {"UsernameAttribute", confData.userNameAttribute},
187 {"GroupsAttribute", confData.groupAttribute}}}}},
188 };
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600189
Ed Tanous81ce6092020-12-17 16:54:55 +0000190 jsonResponse[ldapType].update(ldap);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600191
Ed Tanous81ce6092020-12-17 16:54:55 +0000192 nlohmann::json& roleMapArray = jsonResponse[ldapType]["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600193 roleMapArray = nlohmann::json::array();
194 for (auto& obj : confData.groupRoleList)
195 {
196 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
197 << obj.second.groupName << "\n";
198 roleMapArray.push_back(
199 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
200 nlohmann::json::array(
201 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
202 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530203}
204
205/**
Ratan Gupta06785242019-07-26 22:30:16 +0530206 * @brief validates given JSON input and then calls appropriate method to
207 * create, to delete or to set Rolemapping object based on the given input.
208 *
209 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000210inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800211 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530212 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700213 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530214{
215 for (size_t index = 0; index < input.size(); index++)
216 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700217 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530218
219 if (thisJson.is_null())
220 {
221 // delete the existing object
222 if (index < roleMapObjData.size())
223 {
224 crow::connections::systemBus->async_method_call(
225 [asyncResp, roleMapObjData, serverType,
226 index](const boost::system::error_code ec) {
227 if (ec)
228 {
229 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
230 messages::internalError(asyncResp->res);
231 return;
232 }
233 asyncResp->res
234 .jsonValue[serverType]["RemoteRoleMapping"][index] =
235 nullptr;
236 },
237 ldapDbusService, roleMapObjData[index].first,
238 "xyz.openbmc_project.Object.Delete", "Delete");
239 }
240 else
241 {
242 BMCWEB_LOG_ERROR << "Can't delete the object";
243 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800244 asyncResp->res,
245 thisJson.dump(2, ' ', true,
246 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530247 "RemoteRoleMapping/" + std::to_string(index));
248 return;
249 }
250 }
251 else if (thisJson.empty())
252 {
253 // Don't do anything for the empty objects,parse next json
254 // eg {"RemoteRoleMapping",[{}]}
255 }
256 else
257 {
258 // update/create the object
259 std::optional<std::string> remoteGroup;
260 std::optional<std::string> localRole;
261
Ed Tanousf23b7292020-10-15 09:41:17 -0700262 // This is a copy, but it's required in this case because of how
263 // readJson is structured
264 nlohmann::json thisJsonCopy = thisJson;
265 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
266 "RemoteGroup", remoteGroup, "LocalRole",
267 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530268 {
269 continue;
270 }
271
272 // Update existing RoleMapping Object
273 if (index < roleMapObjData.size())
274 {
275 BMCWEB_LOG_DEBUG << "Update Role Map Object";
276 // If "RemoteGroup" info is provided
277 if (remoteGroup)
278 {
279 crow::connections::systemBus->async_method_call(
280 [asyncResp, roleMapObjData, serverType, index,
281 remoteGroup](const boost::system::error_code ec) {
282 if (ec)
283 {
284 BMCWEB_LOG_ERROR << "DBUS response error: "
285 << ec;
286 messages::internalError(asyncResp->res);
287 return;
288 }
289 asyncResp->res
290 .jsonValue[serverType]["RemoteRoleMapping"]
291 [index]["RemoteGroup"] = *remoteGroup;
292 },
293 ldapDbusService, roleMapObjData[index].first,
294 propertyInterface, "Set",
295 "xyz.openbmc_project.User.PrivilegeMapperEntry",
296 "GroupName",
297 std::variant<std::string>(std::move(*remoteGroup)));
298 }
299
300 // If "LocalRole" info is provided
301 if (localRole)
302 {
303 crow::connections::systemBus->async_method_call(
304 [asyncResp, roleMapObjData, serverType, index,
305 localRole](const boost::system::error_code ec) {
306 if (ec)
307 {
308 BMCWEB_LOG_ERROR << "DBUS response error: "
309 << ec;
310 messages::internalError(asyncResp->res);
311 return;
312 }
313 asyncResp->res
314 .jsonValue[serverType]["RemoteRoleMapping"]
315 [index]["LocalRole"] = *localRole;
316 },
317 ldapDbusService, roleMapObjData[index].first,
318 propertyInterface, "Set",
319 "xyz.openbmc_project.User.PrivilegeMapperEntry",
320 "Privilege",
321 std::variant<std::string>(
322 getPrivilegeFromRoleId(std::move(*localRole))));
323 }
324 }
325 // Create a new RoleMapping Object.
326 else
327 {
328 BMCWEB_LOG_DEBUG
329 << "setRoleMappingProperties: Creating new Object";
330 std::string pathString =
331 "RemoteRoleMapping/" + std::to_string(index);
332
333 if (!localRole)
334 {
335 messages::propertyMissing(asyncResp->res,
336 pathString + "/LocalRole");
337 continue;
338 }
339 if (!remoteGroup)
340 {
341 messages::propertyMissing(asyncResp->res,
342 pathString + "/RemoteGroup");
343 continue;
344 }
345
346 std::string dbusObjectPath;
347 if (serverType == "ActiveDirectory")
348 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700349 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530350 }
351 else if (serverType == "LDAP")
352 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000353 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530354 }
355
356 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
357 << ",LocalRole=" << *localRole;
358
359 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700360 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530361 remoteGroup](const boost::system::error_code ec) {
362 if (ec)
363 {
364 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
365 messages::internalError(asyncResp->res);
366 return;
367 }
368 nlohmann::json& remoteRoleJson =
369 asyncResp->res
370 .jsonValue[serverType]["RemoteRoleMapping"];
371 remoteRoleJson.push_back(
372 {{"LocalRole", *localRole},
373 {"RemoteGroup", *remoteGroup}});
374 },
375 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700376 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530377 getPrivilegeFromRoleId(std::move(*localRole)));
378 }
379 }
380 }
381}
382
383/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530384 * Function that retrieves all properties for LDAP config object
385 * into JSON
386 */
387template <typename CallbackFunc>
388inline void getLDAPConfigData(const std::string& ldapType,
389 CallbackFunc&& callback)
390{
Ratan Guptaab828d72019-04-22 14:18:33 +0530391
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600392 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530393 ldapConfigInterface};
394
395 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600396 [callback, ldapType](const boost::system::error_code ec,
397 const GetObjectType& resp) {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600398 if (ec || resp.empty())
399 {
George Liu0fda0f12021-11-16 10:06:17 +0800400 BMCWEB_LOG_ERROR
401 << "DBUS response error during getting of service name: "
402 << ec;
Ed Tanous23a21a12020-07-25 04:45:05 +0000403 LDAPConfigData empty{};
404 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600405 return;
406 }
407 std::string service = resp.begin()->first;
408 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000409 [callback, ldapType](const boost::system::error_code errorCode,
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600410 const ManagedObjectType& ldapObjects) {
411 LDAPConfigData confData{};
Ed Tanous81ce6092020-12-17 16:54:55 +0000412 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600413 {
414 callback(false, confData, ldapType);
415 BMCWEB_LOG_ERROR << "D-Bus responses error: "
Ed Tanous81ce6092020-12-17 16:54:55 +0000416 << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600417 return;
418 }
419
420 std::string ldapDbusType;
421 std::string searchString;
422
423 if (ldapType == "LDAP")
424 {
George Liu0fda0f12021-11-16 10:06:17 +0800425 ldapDbusType =
426 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600427 searchString = "openldap";
428 }
429 else if (ldapType == "ActiveDirectory")
430 {
431 ldapDbusType =
George Liu0fda0f12021-11-16 10:06:17 +0800432 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600433 searchString = "active_directory";
434 }
435 else
436 {
437 BMCWEB_LOG_ERROR
438 << "Can't get the DbusType for the given type="
439 << ldapType;
440 callback(false, confData, ldapType);
441 return;
442 }
443
444 std::string ldapEnableInterfaceStr = ldapEnableInterface;
445 std::string ldapConfigInterfaceStr = ldapConfigInterface;
446
447 for (const auto& object : ldapObjects)
448 {
449 // let's find the object whose ldap type is equal to the
450 // given type
451 if (object.first.str.find(searchString) ==
452 std::string::npos)
453 {
454 continue;
455 }
456
457 for (const auto& interface : object.second)
458 {
459 if (interface.first == ldapEnableInterfaceStr)
460 {
461 // rest of the properties are string.
462 for (const auto& property : interface.second)
463 {
464 if (property.first == "Enabled")
465 {
466 const bool* value =
467 std::get_if<bool>(&property.second);
468 if (value == nullptr)
469 {
470 continue;
471 }
472 confData.serviceEnabled = *value;
473 break;
474 }
475 }
476 }
477 else if (interface.first == ldapConfigInterfaceStr)
478 {
479
480 for (const auto& property : interface.second)
481 {
Ed Tanous271584a2019-07-09 16:24:22 -0700482 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600483 std::get_if<std::string>(
484 &property.second);
Ed Tanous271584a2019-07-09 16:24:22 -0700485 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600486 {
487 continue;
488 }
489 if (property.first == "LDAPServerURI")
490 {
Ed Tanous271584a2019-07-09 16:24:22 -0700491 confData.uri = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600492 }
493 else if (property.first == "LDAPBindDN")
494 {
Ed Tanous271584a2019-07-09 16:24:22 -0700495 confData.bindDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600496 }
497 else if (property.first == "LDAPBaseDN")
498 {
Ed Tanous271584a2019-07-09 16:24:22 -0700499 confData.baseDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600500 }
501 else if (property.first ==
502 "LDAPSearchScope")
503 {
Ed Tanous271584a2019-07-09 16:24:22 -0700504 confData.searchScope = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600505 }
506 else if (property.first ==
507 "GroupNameAttribute")
508 {
Ed Tanous271584a2019-07-09 16:24:22 -0700509 confData.groupAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600510 }
511 else if (property.first ==
512 "UserNameAttribute")
513 {
Ed Tanous271584a2019-07-09 16:24:22 -0700514 confData.userNameAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600515 }
516 else if (property.first == "LDAPType")
517 {
Ed Tanous271584a2019-07-09 16:24:22 -0700518 confData.serverType = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600519 }
520 }
521 }
George Liu0fda0f12021-11-16 10:06:17 +0800522 else if (
523 interface.first ==
524 "xyz.openbmc_project.User.PrivilegeMapperEntry")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600525 {
526 LDAPRoleMapData roleMapData{};
527 for (const auto& property : interface.second)
528 {
Ed Tanous271584a2019-07-09 16:24:22 -0700529 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600530 std::get_if<std::string>(
531 &property.second);
532
Ed Tanous271584a2019-07-09 16:24:22 -0700533 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600534 {
535 continue;
536 }
537
538 if (property.first == "GroupName")
539 {
Ed Tanous271584a2019-07-09 16:24:22 -0700540 roleMapData.groupName = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600541 }
542 else if (property.first == "Privilege")
543 {
Ed Tanous271584a2019-07-09 16:24:22 -0700544 roleMapData.privilege = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600545 }
546 }
547
Ed Tanous0f0353b2019-10-24 11:37:51 -0700548 confData.groupRoleList.emplace_back(
549 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600550 }
551 }
552 }
553 callback(true, confData, ldapType);
554 },
555 service, ldapRootObject, dbusObjManagerIntf,
556 "GetManagedObjects");
557 },
558 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
Ed Tanous23a21a12020-07-25 04:45:05 +0000559 ldapConfigObjectName, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530560}
561
Ed Tanous6c51eab2021-06-03 12:30:29 -0700562/**
563 * @brief parses the authentication section under the LDAP
564 * @param input JSON data
565 * @param asyncResp pointer to the JSON response
566 * @param userName userName to be filled from the given JSON.
567 * @param password password to be filled from the given JSON.
568 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700569inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700570 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
571 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700572{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700573 std::optional<std::string> authType;
574
575 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
576 authType, "Username", username, "Password",
577 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700579 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700581 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530582 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700583 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530584 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700585 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530586 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700587 messages::propertyValueNotInList(asyncResp->res, *authType,
588 "AuthenticationType");
589 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530590 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700591}
592/**
593 * @brief parses the LDAPService section under the LDAP
594 * @param input JSON data
595 * @param asyncResp pointer to the JSON response
596 * @param baseDNList baseDN to be filled from the given JSON.
597 * @param userNameAttribute userName to be filled from the given JSON.
598 * @param groupaAttribute password to be filled from the given JSON.
599 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530600
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700601inline void
602 parseLDAPServiceJson(nlohmann::json input,
603 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
604 std::optional<std::vector<std::string>>& baseDNList,
605 std::optional<std::string>& userNameAttribute,
606 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700607{
608 std::optional<nlohmann::json> searchSettings;
609
610 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
611 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530612 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700613 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530614 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700615 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530616 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700617 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530618 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700619 if (!json_util::readJson(*searchSettings, asyncResp->res,
620 "BaseDistinguishedNames", baseDNList,
621 "UsernameAttribute", userNameAttribute,
622 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530623 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700624 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530625 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700626}
627/**
628 * @brief updates the LDAP server address and updates the
629 json response with the new value.
630 * @param serviceAddressList address to be updated.
631 * @param asyncResp pointer to the JSON response
632 * @param ldapServerElementName Type of LDAP
633 server(openLDAP/ActiveDirectory)
634 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530635
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700636inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700637 const std::vector<std::string>& serviceAddressList,
638 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
639 const std::string& ldapServerElementName,
640 const std::string& ldapConfigObject)
641{
642 crow::connections::systemBus->async_method_call(
643 [asyncResp, ldapServerElementName,
644 serviceAddressList](const boost::system::error_code ec) {
645 if (ec)
646 {
647 BMCWEB_LOG_DEBUG
648 << "Error Occurred in updating the service address";
649 messages::internalError(asyncResp->res);
650 return;
651 }
652 std::vector<std::string> modifiedserviceAddressList = {
653 serviceAddressList.front()};
654 asyncResp->res
655 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
656 modifiedserviceAddressList;
657 if ((serviceAddressList).size() > 1)
658 {
659 messages::propertyValueModified(asyncResp->res,
660 "ServiceAddresses",
661 serviceAddressList.front());
662 }
663 BMCWEB_LOG_DEBUG << "Updated the service address";
664 },
665 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
666 ldapConfigInterface, "LDAPServerURI",
667 std::variant<std::string>(serviceAddressList.front()));
668}
669/**
670 * @brief updates the LDAP Bind DN and updates the
671 json response with the new value.
672 * @param username name of the user which needs to be updated.
673 * @param asyncResp pointer to the JSON response
674 * @param ldapServerElementName Type of LDAP
675 server(openLDAP/ActiveDirectory)
676 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530677
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700678inline void
679 handleUserNamePatch(const std::string& username,
680 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
681 const std::string& ldapServerElementName,
682 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700683{
684 crow::connections::systemBus->async_method_call(
685 [asyncResp, username,
686 ldapServerElementName](const boost::system::error_code ec) {
687 if (ec)
688 {
689 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
690 messages::internalError(asyncResp->res);
691 return;
692 }
693 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
694 ["Username"] = username;
695 BMCWEB_LOG_DEBUG << "Updated the username";
696 },
697 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
698 ldapConfigInterface, "LDAPBindDN", std::variant<std::string>(username));
699}
700
701/**
702 * @brief updates the LDAP password
703 * @param password : ldap password which needs to be updated.
704 * @param asyncResp pointer to the JSON response
705 * @param ldapServerElementName Type of LDAP
706 * server(openLDAP/ActiveDirectory)
707 */
708
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700709inline void
710 handlePasswordPatch(const std::string& password,
711 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
712 const std::string& ldapServerElementName,
713 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700714{
715 crow::connections::systemBus->async_method_call(
716 [asyncResp, password,
717 ldapServerElementName](const boost::system::error_code ec) {
718 if (ec)
719 {
720 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
721 messages::internalError(asyncResp->res);
722 return;
723 }
724 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
725 ["Password"] = "";
726 BMCWEB_LOG_DEBUG << "Updated the password";
727 },
728 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
729 ldapConfigInterface, "LDAPBindDNPassword",
730 std::variant<std::string>(password));
731}
732
733/**
734 * @brief updates the LDAP BaseDN and updates the
735 json response with the new value.
736 * @param baseDNList baseDN list which needs to be updated.
737 * @param asyncResp pointer to the JSON response
738 * @param ldapServerElementName Type of LDAP
739 server(openLDAP/ActiveDirectory)
740 */
741
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700742inline void
743 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
744 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
745 const std::string& ldapServerElementName,
746 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700747{
748 crow::connections::systemBus->async_method_call(
749 [asyncResp, baseDNList,
750 ldapServerElementName](const boost::system::error_code ec) {
751 if (ec)
752 {
753 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
754 messages::internalError(asyncResp->res);
755 return;
756 }
757 auto& serverTypeJson =
758 asyncResp->res.jsonValue[ldapServerElementName];
759 auto& searchSettingsJson =
760 serverTypeJson["LDAPService"]["SearchSettings"];
761 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
762 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
763 if (baseDNList.size() > 1)
764 {
765 messages::propertyValueModified(asyncResp->res,
766 "BaseDistinguishedNames",
767 baseDNList.front());
768 }
769 BMCWEB_LOG_DEBUG << "Updated the base DN";
770 },
771 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
772 ldapConfigInterface, "LDAPBaseDN",
773 std::variant<std::string>(baseDNList.front()));
774}
775/**
776 * @brief updates the LDAP user name attribute and updates the
777 json response with the new value.
778 * @param userNameAttribute attribute to be updated.
779 * @param asyncResp pointer to the JSON response
780 * @param ldapServerElementName Type of LDAP
781 server(openLDAP/ActiveDirectory)
782 */
783
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700784inline void
785 handleUserNameAttrPatch(const std::string& userNameAttribute,
786 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
787 const std::string& ldapServerElementName,
788 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700789{
790 crow::connections::systemBus->async_method_call(
791 [asyncResp, userNameAttribute,
792 ldapServerElementName](const boost::system::error_code ec) {
793 if (ec)
794 {
795 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
796 "username attribute";
797 messages::internalError(asyncResp->res);
798 return;
799 }
800 auto& serverTypeJson =
801 asyncResp->res.jsonValue[ldapServerElementName];
802 auto& searchSettingsJson =
803 serverTypeJson["LDAPService"]["SearchSettings"];
804 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
805 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
806 },
807 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
808 ldapConfigInterface, "UserNameAttribute",
809 std::variant<std::string>(userNameAttribute));
810}
811/**
812 * @brief updates the LDAP group attribute and updates the
813 json response with the new value.
814 * @param groupsAttribute attribute to be updated.
815 * @param asyncResp pointer to the JSON response
816 * @param ldapServerElementName Type of LDAP
817 server(openLDAP/ActiveDirectory)
818 */
819
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700820inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700821 const std::string& groupsAttribute,
822 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
823 const std::string& ldapServerElementName,
824 const std::string& ldapConfigObject)
825{
826 crow::connections::systemBus->async_method_call(
827 [asyncResp, groupsAttribute,
828 ldapServerElementName](const boost::system::error_code ec) {
829 if (ec)
830 {
831 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
832 "groupname attribute";
833 messages::internalError(asyncResp->res);
834 return;
835 }
836 auto& serverTypeJson =
837 asyncResp->res.jsonValue[ldapServerElementName];
838 auto& searchSettingsJson =
839 serverTypeJson["LDAPService"]["SearchSettings"];
840 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
841 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
842 },
843 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
844 ldapConfigInterface, "GroupNameAttribute",
845 std::variant<std::string>(groupsAttribute));
846}
847/**
848 * @brief updates the LDAP service enable and updates the
849 json response with the new value.
850 * @param input JSON data.
851 * @param asyncResp pointer to the JSON response
852 * @param ldapServerElementName Type of LDAP
853 server(openLDAP/ActiveDirectory)
854 */
855
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700856inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700857 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
858 const std::string& ldapServerElementName,
859 const std::string& ldapConfigObject)
860{
861 crow::connections::systemBus->async_method_call(
862 [asyncResp, serviceEnabled,
863 ldapServerElementName](const boost::system::error_code ec) {
864 if (ec)
865 {
866 BMCWEB_LOG_DEBUG
867 << "Error Occurred in Updating the service enable";
868 messages::internalError(asyncResp->res);
869 return;
870 }
871 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
872 serviceEnabled;
873 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
874 },
875 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
876 ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
877}
878
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700879inline void
880 handleAuthMethodsPatch(nlohmann::json& input,
881 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700882{
883 std::optional<bool> basicAuth;
884 std::optional<bool> cookie;
885 std::optional<bool> sessionToken;
886 std::optional<bool> xToken;
887 std::optional<bool> tls;
888
889 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
890 "Cookie", cookie, "SessionToken", sessionToken,
891 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530892 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700893 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
894 return;
895 }
896
897 // Make a copy of methods configuration
898 persistent_data::AuthConfigMethods authMethodsConfig =
899 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
900
901 if (basicAuth)
902 {
903#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
904 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800905 asyncResp->res,
906 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700907 return;
908#endif
909 authMethodsConfig.basic = *basicAuth;
910 }
911
912 if (cookie)
913 {
914#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800915 messages::actionNotSupported(
916 asyncResp->res,
917 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700918 return;
919#endif
920 authMethodsConfig.cookie = *cookie;
921 }
922
923 if (sessionToken)
924 {
925#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
926 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800927 asyncResp->res,
928 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700929 return;
930#endif
931 authMethodsConfig.sessionToken = *sessionToken;
932 }
933
934 if (xToken)
935 {
936#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800937 messages::actionNotSupported(
938 asyncResp->res,
939 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700940 return;
941#endif
942 authMethodsConfig.xtoken = *xToken;
943 }
944
945 if (tls)
946 {
947#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800948 messages::actionNotSupported(
949 asyncResp->res,
950 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700951 return;
952#endif
953 authMethodsConfig.tls = *tls;
954 }
955
956 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
957 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
958 !authMethodsConfig.tls)
959 {
960 // Do not allow user to disable everything
961 messages::actionNotSupported(asyncResp->res,
962 "of disabling all available methods");
963 return;
964 }
965
966 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
967 authMethodsConfig);
968 // Save configuration immediately
969 persistent_data::getConfig().writeData();
970
971 messages::success(asyncResp->res);
972}
973
974/**
975 * @brief Get the required values from the given JSON, validates the
976 * value and create the LDAP config object.
977 * @param input JSON data
978 * @param asyncResp pointer to the JSON response
979 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
980 */
981
982inline void handleLDAPPatch(nlohmann::json& input,
983 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
984 const std::string& serverType)
985{
986 std::string dbusObjectPath;
987 if (serverType == "ActiveDirectory")
988 {
989 dbusObjectPath = adConfigObject;
990 }
991 else if (serverType == "LDAP")
992 {
993 dbusObjectPath = ldapConfigObjectName;
994 }
995 else
996 {
997 return;
998 }
999
1000 std::optional<nlohmann::json> authentication;
1001 std::optional<nlohmann::json> ldapService;
1002 std::optional<std::vector<std::string>> serviceAddressList;
1003 std::optional<bool> serviceEnabled;
1004 std::optional<std::vector<std::string>> baseDNList;
1005 std::optional<std::string> userNameAttribute;
1006 std::optional<std::string> groupsAttribute;
1007 std::optional<std::string> userName;
1008 std::optional<std::string> password;
1009 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1010
1011 if (!json_util::readJson(input, asyncResp->res, "Authentication",
1012 authentication, "LDAPService", ldapService,
1013 "ServiceAddresses", serviceAddressList,
1014 "ServiceEnabled", serviceEnabled,
1015 "RemoteRoleMapping", remoteRoleMapData))
1016 {
1017 return;
1018 }
1019
1020 if (authentication)
1021 {
1022 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1023 password);
1024 }
1025 if (ldapService)
1026 {
1027 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1028 userNameAttribute, groupsAttribute);
1029 }
1030 if (serviceAddressList)
1031 {
1032 if ((*serviceAddressList).size() == 0)
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301033 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001034 messages::propertyValueNotInList(asyncResp->res, "[]",
1035 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001036 return;
1037 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001038 }
1039 if (baseDNList)
1040 {
1041 if ((*baseDNList).size() == 0)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301042 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001043 messages::propertyValueNotInList(asyncResp->res, "[]",
1044 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301045 return;
1046 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001047 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301048
Ed Tanous6c51eab2021-06-03 12:30:29 -07001049 // nothing to update, then return
1050 if (!userName && !password && !serviceAddressList && !baseDNList &&
1051 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1052 !remoteRoleMapData)
1053 {
1054 return;
1055 }
1056
1057 // Get the existing resource first then keep modifying
1058 // whenever any property gets updated.
1059 getLDAPConfigData(serverType, [asyncResp, userName, password, baseDNList,
1060 userNameAttribute, groupsAttribute,
1061 serviceAddressList, serviceEnabled,
1062 dbusObjectPath, remoteRoleMapData](
1063 bool success,
1064 const LDAPConfigData& confData,
1065 const std::string& serverT) {
1066 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301067 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001068 messages::internalError(asyncResp->res);
1069 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301070 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001071 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1072 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301073 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001074 // Disable the service first and update the rest of
1075 // the properties.
1076 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301077 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001078
Ratan Gupta8a07d282019-03-16 08:33:47 +05301079 if (serviceAddressList)
1080 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001081 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1082 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301083 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001084 if (userName)
1085 {
1086 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1087 }
1088 if (password)
1089 {
1090 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1091 }
1092
Ratan Gupta8a07d282019-03-16 08:33:47 +05301093 if (baseDNList)
1094 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001095 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1096 }
1097 if (userNameAttribute)
1098 {
1099 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1100 dbusObjectPath);
1101 }
1102 if (groupsAttribute)
1103 {
1104 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1105 dbusObjectPath);
1106 }
1107 if (serviceEnabled)
1108 {
1109 // if user has given the value as true then enable
1110 // the service. if user has given false then no-op
1111 // as service is already stopped.
1112 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301113 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001114 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1115 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301116 }
1117 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001118 else
1119 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001120 // if user has not given the service enabled value
1121 // then revert it to the same state as it was
1122 // before.
1123 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1124 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001125 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001126
Ed Tanous6c51eab2021-06-03 12:30:29 -07001127 if (remoteRoleMapData)
1128 {
1129 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1130 *remoteRoleMapData);
1131 }
1132 });
1133}
1134
1135inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1136 const std::string& username,
1137 std::optional<std::string> password,
1138 std::optional<bool> enabled,
1139 std::optional<std::string> roleId,
1140 std::optional<bool> locked)
1141{
1142 std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
1143 dbus::utility::escapePathForDbus(dbusObjectPath);
1144
1145 dbus::utility::checkDbusPathExists(
1146 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001147 [dbusObjectPath, username, password(std::move(password)),
1148 roleId(std::move(roleId)), enabled, locked,
1149 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001150 if (!rc)
1151 {
1152 messages::resourceNotFound(
1153 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1154 username);
1155 return;
1156 }
1157
1158 if (password)
1159 {
1160 int retval = pamUpdatePassword(username, *password);
1161
1162 if (retval == PAM_USER_UNKNOWN)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001163 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001164 messages::resourceNotFound(
1165 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1166 username);
1167 }
1168 else if (retval == PAM_AUTHTOK_ERR)
1169 {
1170 // If password is invalid
1171 messages::propertyValueFormatError(asyncResp->res,
1172 *password, "Password");
1173 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1174 }
1175 else if (retval != PAM_SUCCESS)
1176 {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001177 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001178 return;
1179 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001180 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001181
Ed Tanous6c51eab2021-06-03 12:30:29 -07001182 if (enabled)
1183 {
1184 crow::connections::systemBus->async_method_call(
1185 [asyncResp](const boost::system::error_code ec) {
1186 if (ec)
1187 {
1188 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1189 messages::internalError(asyncResp->res);
1190 return;
1191 }
1192 messages::success(asyncResp->res);
1193 return;
1194 },
1195 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1196 "org.freedesktop.DBus.Properties", "Set",
1197 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1198 std::variant<bool>{*enabled});
1199 }
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001200
Ed Tanous6c51eab2021-06-03 12:30:29 -07001201 if (roleId)
1202 {
1203 std::string priv = getPrivilegeFromRoleId(*roleId);
1204 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001205 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001206 messages::propertyValueNotInList(asyncResp->res, *roleId,
1207 "RoleId");
1208 return;
1209 }
1210 if (priv == "priv-noaccess")
1211 {
1212 priv = "";
1213 }
1214
1215 crow::connections::systemBus->async_method_call(
1216 [asyncResp](const boost::system::error_code ec) {
1217 if (ec)
1218 {
1219 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1220 messages::internalError(asyncResp->res);
1221 return;
1222 }
1223 messages::success(asyncResp->res);
1224 },
1225 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1226 "org.freedesktop.DBus.Properties", "Set",
1227 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1228 std::variant<std::string>{priv});
1229 }
1230
1231 if (locked)
1232 {
1233 // admin can unlock the account which is locked by
1234 // successive authentication failures but admin should
1235 // not be allowed to lock an account.
1236 if (*locked)
1237 {
1238 messages::propertyValueNotInList(asyncResp->res, "true",
1239 "Locked");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001240 return;
1241 }
1242
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001243 crow::connections::systemBus->async_method_call(
Ed Tanous6c51eab2021-06-03 12:30:29 -07001244 [asyncResp](const boost::system::error_code ec) {
1245 if (ec)
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001246 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001247 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1248 messages::internalError(asyncResp->res);
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001249 return;
1250 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001251 messages::success(asyncResp->res);
1252 return;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001253 },
Ed Tanous6c51eab2021-06-03 12:30:29 -07001254 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1255 "org.freedesktop.DBus.Properties", "Set",
1256 "xyz.openbmc_project.User.Attributes",
1257 "UserLockedForFailedAttempt", std::variant<bool>{*locked});
1258 }
1259 });
1260}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001261
Ed Tanous6c51eab2021-06-03 12:30:29 -07001262inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001263{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001264
Ed Tanous6c51eab2021-06-03 12:30:29 -07001265 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001266 .privileges(redfish::privileges::getAccountService)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001267 .methods(
Abhishek Patel72048782021-06-02 09:53:24 -05001268 boost::beast::http::verb::get)([](const crow::Request& req,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001269 const std::shared_ptr<
1270 bmcweb::AsyncResp>& asyncResp)
1271 -> void {
1272 const persistent_data::AuthConfigMethods& authMethodsConfig =
1273 persistent_data::SessionStore::getInstance()
1274 .getAuthMethodsConfig();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001275
Ed Tanous6c51eab2021-06-03 12:30:29 -07001276 asyncResp->res.jsonValue = {
1277 {"@odata.id", "/redfish/v1/AccountService"},
1278 {"@odata.type", "#AccountService."
1279 "v1_5_0.AccountService"},
1280 {"Id", "AccountService"},
1281 {"Name", "Account Service"},
1282 {"Description", "Account Service"},
1283 {"ServiceEnabled", true},
1284 {"MaxPasswordLength", 20},
1285 {"Accounts",
1286 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
1287 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
1288 {"Oem",
1289 {{"OpenBMC",
1290 {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
Ed Tanousd8e3c292021-09-21 18:01:43 -07001291 {"@odata.id", "/redfish/v1/AccountService#/Oem/OpenBMC"},
Ed Tanous6c51eab2021-06-03 12:30:29 -07001292 {"AuthMethods",
1293 {
1294 {"BasicAuth", authMethodsConfig.basic},
1295 {"SessionToken", authMethodsConfig.sessionToken},
1296 {"XToken", authMethodsConfig.xtoken},
1297 {"Cookie", authMethodsConfig.cookie},
1298 {"TLS", authMethodsConfig.tls},
Abhishek Patel72048782021-06-02 09:53:24 -05001299 }}}}}}};
1300 // /redfish/v1/AccountService/LDAP/Certificates is something only
1301 // ConfigureManager can access then only display when the user has
1302 // permissions ConfigureManager
1303 Privileges effectiveUserPrivileges =
1304 redfish::getUserPrivileges(req.userRole);
1305
1306 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1307 effectiveUserPrivileges))
1308 {
1309 asyncResp->res.jsonValue["LDAP"] = {
1310 {"Certificates",
1311 {{"@odata.id",
1312 "/redfish/v1/AccountService/LDAP/Certificates"}}}};
1313 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001314 crow::connections::systemBus->async_method_call(
1315 [asyncResp](
1316 const boost::system::error_code ec,
1317 const std::vector<
1318 std::pair<std::string,
1319 std::variant<uint32_t, uint16_t, uint8_t>>>&
1320 propertiesList) {
1321 if (ec)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001322 {
1323 messages::internalError(asyncResp->res);
1324 return;
1325 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001326 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1327 << "properties for AccountService";
1328 for (const std::pair<std::string,
1329 std::variant<uint32_t, uint16_t,
1330 uint8_t>>& property :
1331 propertiesList)
1332 {
1333 if (property.first == "MinPasswordLength")
1334 {
1335 const uint8_t* value =
1336 std::get_if<uint8_t>(&property.second);
1337 if (value != nullptr)
Ratan Gupta24c85422019-01-30 19:41:24 +05301338 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001339 asyncResp->res.jsonValue["MinPasswordLength"] =
1340 *value;
Ratan Gupta24c85422019-01-30 19:41:24 +05301341 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001342 }
1343 if (property.first == "AccountUnlockTimeout")
1344 {
1345 const uint32_t* value =
1346 std::get_if<uint32_t>(&property.second);
1347 if (value != nullptr)
1348 {
1349 asyncResp->res
1350 .jsonValue["AccountLockoutDuration"] =
1351 *value;
1352 }
1353 }
1354 if (property.first == "MaxLoginAttemptBeforeLockout")
1355 {
1356 const uint16_t* value =
1357 std::get_if<uint16_t>(&property.second);
1358 if (value != nullptr)
1359 {
1360 asyncResp->res
1361 .jsonValue["AccountLockoutThreshold"] =
1362 *value;
1363 }
1364 }
1365 }
1366 },
1367 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1368 "org.freedesktop.DBus.Properties", "GetAll",
1369 "xyz.openbmc_project.User.AccountPolicy");
1370
1371 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
1372 const std::string& ldapType) {
1373 if (!success)
1374 {
1375 return;
1376 }
1377 parseLDAPConfigData(asyncResp->res.jsonValue, confData,
1378 ldapType);
1379 };
1380
1381 getLDAPConfigData("LDAP", callback);
1382 getLDAPConfigData("ActiveDirectory", callback);
1383 });
1384
Ed Tanousf5ffd802021-07-19 10:55:33 -07001385 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
1386 .privileges(redfish::privileges::getAccountService)
1387 .methods(boost::beast::http::verb::patch)(
1388 [](const crow::Request& req,
1389 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1390 std::optional<uint32_t> unlockTimeout;
1391 std::optional<uint16_t> lockoutThreshold;
1392 std::optional<uint16_t> minPasswordLength;
1393 std::optional<uint16_t> maxPasswordLength;
1394 std::optional<nlohmann::json> ldapObject;
1395 std::optional<nlohmann::json> activeDirectoryObject;
1396 std::optional<nlohmann::json> oemObject;
1397
1398 if (!json_util::readJson(
1399 req, asyncResp->res, "AccountLockoutDuration",
1400 unlockTimeout, "AccountLockoutThreshold",
1401 lockoutThreshold, "MaxPasswordLength",
1402 maxPasswordLength, "MinPasswordLength",
1403 minPasswordLength, "LDAP", ldapObject,
1404 "ActiveDirectory", activeDirectoryObject, "Oem",
1405 oemObject))
1406 {
1407 return;
1408 }
1409
1410 if (minPasswordLength)
1411 {
1412 messages::propertyNotWritable(asyncResp->res,
1413 "MinPasswordLength");
1414 }
1415
1416 if (maxPasswordLength)
1417 {
1418 messages::propertyNotWritable(asyncResp->res,
1419 "MaxPasswordLength");
1420 }
1421
1422 if (ldapObject)
1423 {
1424 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1425 }
1426
1427 if (std::optional<nlohmann::json> oemOpenBMCObject;
1428 oemObject &&
1429 json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1430 oemOpenBMCObject))
1431 {
1432 if (std::optional<nlohmann::json> authMethodsObject;
1433 oemOpenBMCObject &&
1434 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1435 "AuthMethods", authMethodsObject))
1436 {
1437 if (authMethodsObject)
1438 {
1439 handleAuthMethodsPatch(*authMethodsObject,
1440 asyncResp);
1441 }
1442 }
1443 }
1444
1445 if (activeDirectoryObject)
1446 {
1447 handleLDAPPatch(*activeDirectoryObject, asyncResp,
1448 "ActiveDirectory");
1449 }
1450
1451 if (unlockTimeout)
1452 {
1453 crow::connections::systemBus->async_method_call(
1454 [asyncResp](const boost::system::error_code ec) {
1455 if (ec)
1456 {
1457 messages::internalError(asyncResp->res);
1458 return;
1459 }
1460 messages::success(asyncResp->res);
1461 },
1462 "xyz.openbmc_project.User.Manager",
1463 "/xyz/openbmc_project/user",
1464 "org.freedesktop.DBus.Properties", "Set",
1465 "xyz.openbmc_project.User.AccountPolicy",
1466 "AccountUnlockTimeout",
1467 std::variant<uint32_t>(*unlockTimeout));
1468 }
1469 if (lockoutThreshold)
1470 {
1471 crow::connections::systemBus->async_method_call(
1472 [asyncResp](const boost::system::error_code ec) {
1473 if (ec)
1474 {
1475 messages::internalError(asyncResp->res);
1476 return;
1477 }
1478 messages::success(asyncResp->res);
1479 },
1480 "xyz.openbmc_project.User.Manager",
1481 "/xyz/openbmc_project/user",
1482 "org.freedesktop.DBus.Properties", "Set",
1483 "xyz.openbmc_project.User.AccountPolicy",
1484 "MaxLoginAttemptBeforeLockout",
1485 std::variant<uint16_t>(*lockoutThreshold));
1486 }
1487 });
1488
Ed Tanous6c51eab2021-06-03 12:30:29 -07001489 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001490 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001491 .methods(boost::beast::http::verb::get)(
1492 [](const crow::Request& req,
1493 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1494 asyncResp->res.jsonValue = {
1495 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
1496 {"@odata.type", "#ManagerAccountCollection."
1497 "ManagerAccountCollection"},
1498 {"Name", "Accounts Collection"},
1499 {"Description", "BMC User Accounts"}};
1500
Ed Tanous6c51eab2021-06-03 12:30:29 -07001501 Privileges effectiveUserPrivileges =
1502 redfish::getUserPrivileges(req.userRole);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001503
1504 std::string thisUser = req.session->username;
1505
1506 crow::connections::systemBus->async_method_call(
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001507 [asyncResp, thisUser, effectiveUserPrivileges](
1508 const boost::system::error_code ec,
1509 const ManagedObjectType& users) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001510 if (ec)
1511 {
1512 messages::internalError(asyncResp->res);
Ratan Gupta24c85422019-01-30 19:41:24 +05301513 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001514 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001515
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001516 bool userCanSeeAllAccounts =
1517 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001518 {"ConfigureUsers"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001519
1520 bool userCanSeeSelf =
1521 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001522 {"ConfigureSelf"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001523
Ed Tanous6c51eab2021-06-03 12:30:29 -07001524 nlohmann::json& memberArray =
1525 asyncResp->res.jsonValue["Members"];
1526 memberArray = nlohmann::json::array();
Ratan Gupta24c85422019-01-30 19:41:24 +05301527
Ed Tanous6c51eab2021-06-03 12:30:29 -07001528 for (auto& userpath : users)
1529 {
1530 std::string user = userpath.first.filename();
1531 if (user.empty())
Ratan Gupta24c85422019-01-30 19:41:24 +05301532 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301533 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001534 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1535
Ratan Gupta24c85422019-01-30 19:41:24 +05301536 return;
1537 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301538
Ed Tanous6c51eab2021-06-03 12:30:29 -07001539 // As clarified by Redfish here:
1540 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1541 // Users without ConfigureUsers, only see their own
1542 // account. Users with ConfigureUsers, see all
1543 // accounts.
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001544 if (userCanSeeAllAccounts ||
1545 (thisUser == user && userCanSeeSelf))
Ratan Gupta24c85422019-01-30 19:41:24 +05301546 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001547 memberArray.push_back(
1548 {{"@odata.id",
1549 "/redfish/v1/AccountService/Accounts/" +
1550 user}});
Ratan Gupta24c85422019-01-30 19:41:24 +05301551 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001552 }
1553 asyncResp->res.jsonValue["Members@odata.count"] =
1554 memberArray.size();
1555 },
1556 "xyz.openbmc_project.User.Manager",
1557 "/xyz/openbmc_project/user",
1558 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Ratan Gupta24c85422019-01-30 19:41:24 +05301559 });
Ed Tanous06e086d2018-09-19 17:19:52 -07001560
Ed Tanous6c51eab2021-06-03 12:30:29 -07001561 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001562 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001563 .methods(boost::beast::http::verb::post)([](const crow::Request& req,
1564 const std::shared_ptr<
1565 bmcweb::AsyncResp>&
1566 asyncResp) -> void {
1567 std::string username;
1568 std::string password;
1569 std::optional<std::string> roleId("User");
1570 std::optional<bool> enabled = true;
1571 if (!json_util::readJson(req, asyncResp->res, "UserName", username,
1572 "Password", password, "RoleId", roleId,
1573 "Enabled", enabled))
1574 {
1575 return;
1576 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001577
Ed Tanous6c51eab2021-06-03 12:30:29 -07001578 std::string priv = getPrivilegeFromRoleId(*roleId);
1579 if (priv.empty())
1580 {
1581 messages::propertyValueNotInList(asyncResp->res, *roleId,
1582 "RoleId");
1583 return;
1584 }
1585 // TODO: Following override will be reverted once support in
1586 // phosphor-user-manager is added. In order to avoid dependency
1587 // issues, this is added in bmcweb, which will removed, once
1588 // phosphor-user-manager supports priv-noaccess.
1589 if (priv == "priv-noaccess")
1590 {
1591 roleId = "";
1592 }
1593 else
1594 {
1595 roleId = priv;
1596 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001597
Ed Tanous6c51eab2021-06-03 12:30:29 -07001598 // Reading AllGroups property
1599 crow::connections::systemBus->async_method_call(
1600 [asyncResp, username, password{std::move(password)}, roleId,
1601 enabled](
1602 const boost::system::error_code ec,
1603 const std::variant<std::vector<std::string>>& allGroups) {
1604 if (ec)
1605 {
1606 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1607 messages::internalError(asyncResp->res);
1608 return;
1609 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001610
Ed Tanous6c51eab2021-06-03 12:30:29 -07001611 const std::vector<std::string>* allGroupsList =
1612 std::get_if<std::vector<std::string>>(&allGroups);
1613
1614 if (allGroupsList == nullptr || allGroupsList->empty())
1615 {
1616 messages::internalError(asyncResp->res);
1617 return;
1618 }
1619
1620 crow::connections::systemBus->async_method_call(
1621 [asyncResp, username,
1622 password](const boost::system::error_code ec2,
1623 sdbusplus::message::message& m) {
1624 if (ec2)
1625 {
1626 userErrorMessageHandler(
1627 m.get_error(), asyncResp, username, "");
1628 return;
1629 }
1630
1631 if (pamUpdatePassword(username, password) !=
1632 PAM_SUCCESS)
1633 {
1634 // At this point we have a user that's been
1635 // created, but the password set
1636 // failed.Something is wrong, so delete the user
1637 // that we've already created
1638 crow::connections::systemBus->async_method_call(
1639 [asyncResp, password](
1640 const boost::system::error_code ec3) {
1641 if (ec3)
1642 {
1643 messages::internalError(
1644 asyncResp->res);
1645 return;
1646 }
1647
1648 // If password is invalid
1649 messages::propertyValueFormatError(
1650 asyncResp->res, password,
1651 "Password");
1652 },
1653 "xyz.openbmc_project.User.Manager",
1654 "/xyz/openbmc_project/user/" + username,
1655 "xyz.openbmc_project.Object.Delete",
1656 "Delete");
1657
1658 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1659 return;
1660 }
1661
1662 messages::created(asyncResp->res);
1663 asyncResp->res.addHeader(
1664 "Location",
1665 "/redfish/v1/AccountService/Accounts/" +
1666 username);
1667 },
1668 "xyz.openbmc_project.User.Manager",
1669 "/xyz/openbmc_project/user",
1670 "xyz.openbmc_project.User.Manager", "CreateUser",
1671 username, *allGroupsList, *roleId, *enabled);
1672 },
1673 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1674 "org.freedesktop.DBus.Properties", "Get",
1675 "xyz.openbmc_project.User.Manager", "AllGroups");
1676 });
1677
1678 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001679 .privileges(redfish::privileges::getManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001680 .methods(
1681 boost::beast::http::verb::
1682 get)([](const crow::Request& req,
1683 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1684 const std::string& accountName) -> void {
1685 if (req.session->username != accountName)
1686 {
1687 // At this point we've determined that the user is trying to
1688 // modify a user that isn't them. We need to verify that they
1689 // have permissions to modify other users, so re-run the auth
1690 // check with the same permissions, minus ConfigureSelf.
1691 Privileges effectiveUserPrivileges =
1692 redfish::getUserPrivileges(req.userRole);
1693 Privileges requiredPermissionsToChangeNonSelf = {
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001694 "ConfigureUsers", "ConfigureManager"};
Ed Tanous6c51eab2021-06-03 12:30:29 -07001695 if (!effectiveUserPrivileges.isSupersetOf(
1696 requiredPermissionsToChangeNonSelf))
Ed Tanous06e086d2018-09-19 17:19:52 -07001697 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001698 BMCWEB_LOG_DEBUG << "GET Account denied access";
1699 messages::insufficientPrivilege(asyncResp->res);
1700 return;
1701 }
1702 }
1703
1704 crow::connections::systemBus->async_method_call(
1705 [asyncResp, accountName](const boost::system::error_code ec,
1706 const ManagedObjectType& users) {
1707 if (ec)
1708 {
1709 messages::internalError(asyncResp->res);
1710 return;
1711 }
1712 auto userIt = users.begin();
1713
1714 for (; userIt != users.end(); userIt++)
1715 {
1716 if (boost::ends_with(userIt->first.str,
1717 "/" + accountName))
1718 {
1719 break;
1720 }
1721 }
1722 if (userIt == users.end())
1723 {
1724 messages::resourceNotFound(
1725 asyncResp->res, "ManagerAccount", accountName);
1726 return;
1727 }
1728
1729 asyncResp->res.jsonValue = {
1730 {"@odata.type",
1731 "#ManagerAccount.v1_4_0.ManagerAccount"},
1732 {"Name", "User Account"},
1733 {"Description", "User Account"},
1734 {"Password", nullptr},
1735 {"AccountTypes", {"Redfish"}}};
1736
1737 for (const auto& interface : userIt->second)
1738 {
1739 if (interface.first ==
1740 "xyz.openbmc_project.User.Attributes")
1741 {
1742 for (const auto& property : interface.second)
1743 {
1744 if (property.first == "UserEnabled")
1745 {
1746 const bool* userEnabled =
1747 std::get_if<bool>(&property.second);
1748 if (userEnabled == nullptr)
1749 {
1750 BMCWEB_LOG_ERROR
1751 << "UserEnabled wasn't a bool";
1752 messages::internalError(asyncResp->res);
1753 return;
1754 }
1755 asyncResp->res.jsonValue["Enabled"] =
1756 *userEnabled;
1757 }
1758 else if (property.first ==
1759 "UserLockedForFailedAttempt")
1760 {
1761 const bool* userLocked =
1762 std::get_if<bool>(&property.second);
1763 if (userLocked == nullptr)
1764 {
1765 BMCWEB_LOG_ERROR << "UserLockedForF"
1766 "ailedAttempt "
1767 "wasn't a bool";
1768 messages::internalError(asyncResp->res);
1769 return;
1770 }
1771 asyncResp->res.jsonValue["Locked"] =
1772 *userLocked;
1773 asyncResp->res.jsonValue
1774 ["Locked@Redfish.AllowableValues"] = {
1775 "false"}; // can only unlock accounts
1776 }
1777 else if (property.first == "UserPrivilege")
1778 {
1779 const std::string* userPrivPtr =
1780 std::get_if<std::string>(
1781 &property.second);
1782 if (userPrivPtr == nullptr)
1783 {
1784 BMCWEB_LOG_ERROR
1785 << "UserPrivilege wasn't a "
1786 "string";
1787 messages::internalError(asyncResp->res);
1788 return;
1789 }
1790 std::string role =
1791 getRoleIdFromPrivilege(*userPrivPtr);
1792 if (role.empty())
1793 {
1794 BMCWEB_LOG_ERROR << "Invalid user role";
1795 messages::internalError(asyncResp->res);
1796 return;
1797 }
1798 asyncResp->res.jsonValue["RoleId"] = role;
1799
1800 asyncResp->res.jsonValue["Links"]["Role"] =
1801 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +08001802 "/redfish/v1/AccountService/Roles/" +
Ed Tanous6c51eab2021-06-03 12:30:29 -07001803 role}};
1804 }
1805 else if (property.first ==
1806 "UserPasswordExpired")
1807 {
1808 const bool* userPasswordExpired =
1809 std::get_if<bool>(&property.second);
1810 if (userPasswordExpired == nullptr)
1811 {
George Liu0fda0f12021-11-16 10:06:17 +08001812 BMCWEB_LOG_ERROR
1813 << "UserPasswordExpired wasn't a bool";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001814 messages::internalError(asyncResp->res);
1815 return;
1816 }
1817 asyncResp->res
1818 .jsonValue["PasswordChangeRequired"] =
1819 *userPasswordExpired;
1820 }
1821 }
1822 }
1823 }
1824
1825 asyncResp->res.jsonValue["@odata.id"] =
1826 "/redfish/v1/AccountService/Accounts/" + accountName;
1827 asyncResp->res.jsonValue["Id"] = accountName;
1828 asyncResp->res.jsonValue["UserName"] = accountName;
1829 },
1830 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1831 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1832 });
1833
1834 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001835 // TODO this privilege should be using the generated endpoints, but
1836 // because of the special handling of ConfigureSelf, it's not able to
1837 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001838 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1839 .methods(boost::beast::http::verb::patch)(
1840 [](const crow::Request& req,
1841 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1842 const std::string& username) -> void {
1843 std::optional<std::string> newUserName;
1844 std::optional<std::string> password;
1845 std::optional<bool> enabled;
1846 std::optional<std::string> roleId;
1847 std::optional<bool> locked;
Ed Tanouse9cc5172021-11-03 14:13:19 +08001848
1849 Privileges effectiveUserPrivileges =
1850 redfish::getUserPrivileges(req.userRole);
1851 Privileges configureUsers = {"ConfigureUsers"};
1852 bool userHasConfigureUsers =
1853 effectiveUserPrivileges.isSupersetOf(configureUsers);
1854 if (userHasConfigureUsers)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001855 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001856 // Users with ConfigureUsers can modify for all users
1857 if (!json_util::readJson(req, asyncResp->res, "UserName",
1858 newUserName, "Password", password,
1859 "RoleId", roleId, "Enabled",
1860 enabled, "Locked", locked))
1861 {
1862 return;
1863 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001864 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001865 else
Ed Tanous6c51eab2021-06-03 12:30:29 -07001866 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001867 // ConfigureSelf accounts can only modify their own account
1868 if (username != req.session->username)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001869 {
1870 messages::insufficientPrivilege(asyncResp->res);
1871 return;
1872 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001873 // ConfigureSelf accounts can only modify their password
1874 if (!json_util::readJson(req, asyncResp->res, "Password",
1875 password))
1876 {
1877 return;
1878 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001879 }
1880
1881 // if user name is not provided in the patch method or if it
1882 // matches the user name in the URI, then we are treating it as
1883 // updating user properties other then username. If username
1884 // provided doesn't match the URI, then we are treating this as
1885 // user rename request.
1886 if (!newUserName || (newUserName.value() == username))
1887 {
1888 updateUserProperties(asyncResp, username, password, enabled,
1889 roleId, locked);
1890 return;
1891 }
1892 crow::connections::systemBus->async_method_call(
1893 [asyncResp, username, password(std::move(password)),
1894 roleId(std::move(roleId)), enabled,
1895 newUser{std::string(*newUserName)},
1896 locked](const boost::system::error_code ec,
1897 sdbusplus::message::message& m) {
1898 if (ec)
1899 {
1900 userErrorMessageHandler(m.get_error(), asyncResp,
1901 newUser, username);
1902 return;
1903 }
1904
1905 updateUserProperties(asyncResp, newUser, password,
1906 enabled, roleId, locked);
1907 },
1908 "xyz.openbmc_project.User.Manager",
1909 "/xyz/openbmc_project/user",
1910 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1911 *newUserName);
1912 });
1913
1914 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001915 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001916 .methods(boost::beast::http::verb::delete_)(
1917 [](const crow::Request& /*req*/,
1918 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1919 const std::string& username) -> void {
1920 const std::string userPath =
1921 "/xyz/openbmc_project/user/" + username;
1922
1923 crow::connections::systemBus->async_method_call(
1924 [asyncResp, username](const boost::system::error_code ec) {
1925 if (ec)
1926 {
1927 messages::resourceNotFound(
1928 asyncResp->res,
1929 "#ManagerAccount.v1_4_0.ManagerAccount",
1930 username);
1931 return;
1932 }
1933
1934 messages::accountRemoved(asyncResp->res);
1935 },
1936 "xyz.openbmc_project.User.Manager", userPath,
1937 "xyz.openbmc_project.Object.Delete", "Delete");
1938 });
1939}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001940
Ed Tanous1abe55e2018-09-05 08:30:59 -07001941} // namespace redfish