blob: 865a3834f9ca24af0762c5c229388a968d5e2242 [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
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053036constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053037constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
38constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
39constexpr const char* ldapConfigInterface =
40 "xyz.openbmc_project.User.Ldap.Config";
41constexpr const char* ldapCreateInterface =
42 "xyz.openbmc_project.User.Ldap.Create";
43constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053044constexpr const char* ldapPrivMapperInterface =
45 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053046constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
47constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
48constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
49constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
50constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
51
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060052struct LDAPRoleMapData
53{
54 std::string groupName;
55 std::string privilege;
56};
57
Ratan Gupta6973a582018-12-13 18:25:44 +053058struct LDAPConfigData
59{
60 std::string uri{};
61 std::string bindDN{};
62 std::string baseDN{};
63 std::string searchScope{};
64 std::string serverType{};
65 bool serviceEnabled = false;
66 std::string userNameAttribute{};
67 std::string groupAttribute{};
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060068 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053069};
70
Patrick Williams19bd78d2020-05-13 17:38:24 -050071using DbusVariantType = std::variant<bool, int32_t, std::string>;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020072
73using DbusInterfaceType = boost::container::flat_map<
74 std::string, boost::container::flat_map<std::string, DbusVariantType>>;
75
76using ManagedObjectType =
77 std::vector<std::pair<sdbusplus::message::object_path, DbusInterfaceType>>;
78
Ratan Gupta6973a582018-12-13 18:25:44 +053079using GetObjectType =
80 std::vector<std::pair<std::string, std::vector<std::string>>>;
AppaRao Puli84e12cb2018-10-11 01:28:15 +053081
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060082inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053083{
84 if (role == "priv-admin")
85 {
86 return "Administrator";
87 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070088 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053089 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053090 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053091 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070092 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053093 {
94 return "Operator";
95 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070096 if ((role == "") || (role == "priv-noaccess"))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +000097 {
98 return "NoAccess";
99 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530100 return "";
101}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600102inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530103{
104 if (role == "Administrator")
105 {
106 return "priv-admin";
107 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700108 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530109 {
110 return "priv-user";
111 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700112 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530113 {
114 return "priv-operator";
115 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700116 if ((role == "NoAccess") || (role == ""))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +0000117 {
118 return "priv-noaccess";
119 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530120 return "";
121}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700122
zhanghch058d1b46d2021-04-01 11:18:24 +0800123inline void userErrorMessageHandler(
124 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
125 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000126{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000127 if (e == nullptr)
128 {
129 messages::internalError(asyncResp->res);
130 return;
131 }
132
Manojkiran Eda055806b2020-11-03 09:36:28 +0530133 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000134 if (strcmp(errorMessage,
135 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
136 {
137 messages::resourceAlreadyExists(asyncResp->res,
Gunnar Mills8114bd42020-06-11 20:55:21 -0500138 "#ManagerAccount.v1_4_0.ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000139 "UserName", newUser);
140 }
141 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
142 "UserNameDoesNotExist") == 0)
143 {
144 messages::resourceNotFound(
Gunnar Mills8114bd42020-06-11 20:55:21 -0500145 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000146 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700147 else if ((strcmp(errorMessage,
148 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
149 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800150 (strcmp(
151 errorMessage,
152 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
153 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000154 {
155 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
156 }
157 else if (strcmp(errorMessage,
158 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
159 {
160 messages::createLimitReachedForResource(asyncResp->res);
161 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000162 else
163 {
164 messages::internalError(asyncResp->res);
165 }
166
167 return;
168}
169
Ed Tanous81ce6092020-12-17 16:54:55 +0000170inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000171 const LDAPConfigData& confData,
172 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530173{
Ratan Guptaab828d72019-04-22 14:18:33 +0530174 std::string service =
175 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600176 nlohmann::json ldap = {
Ratan Gupta6973a582018-12-13 18:25:44 +0530177 {"ServiceEnabled", confData.serviceEnabled},
178 {"ServiceAddresses", nlohmann::json::array({confData.uri})},
179 {"Authentication",
180 {{"AuthenticationType", "UsernameAndPassword"},
Ratan Gupta6973a582018-12-13 18:25:44 +0530181 {"Username", confData.bindDN},
182 {"Password", nullptr}}},
183 {"LDAPService",
184 {{"SearchSettings",
185 {{"BaseDistinguishedNames",
186 nlohmann::json::array({confData.baseDN})},
187 {"UsernameAttribute", confData.userNameAttribute},
188 {"GroupsAttribute", confData.groupAttribute}}}}},
189 };
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600190
Ed Tanous81ce6092020-12-17 16:54:55 +0000191 jsonResponse[ldapType].update(ldap);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600192
Ed Tanous81ce6092020-12-17 16:54:55 +0000193 nlohmann::json& roleMapArray = jsonResponse[ldapType]["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600194 roleMapArray = nlohmann::json::array();
195 for (auto& obj : confData.groupRoleList)
196 {
197 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
198 << obj.second.groupName << "\n";
199 roleMapArray.push_back(
200 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
201 nlohmann::json::array(
202 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
203 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530204}
205
206/**
Ratan Gupta06785242019-07-26 22:30:16 +0530207 * @brief validates given JSON input and then calls appropriate method to
208 * create, to delete or to set Rolemapping object based on the given input.
209 *
210 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000211inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800212 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530213 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700214 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530215{
216 for (size_t index = 0; index < input.size(); index++)
217 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700218 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530219
220 if (thisJson.is_null())
221 {
222 // delete the existing object
223 if (index < roleMapObjData.size())
224 {
225 crow::connections::systemBus->async_method_call(
226 [asyncResp, roleMapObjData, serverType,
227 index](const boost::system::error_code ec) {
228 if (ec)
229 {
230 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
231 messages::internalError(asyncResp->res);
232 return;
233 }
234 asyncResp->res
235 .jsonValue[serverType]["RemoteRoleMapping"][index] =
236 nullptr;
237 },
238 ldapDbusService, roleMapObjData[index].first,
239 "xyz.openbmc_project.Object.Delete", "Delete");
240 }
241 else
242 {
243 BMCWEB_LOG_ERROR << "Can't delete the object";
244 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800245 asyncResp->res,
246 thisJson.dump(2, ' ', true,
247 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530248 "RemoteRoleMapping/" + std::to_string(index));
249 return;
250 }
251 }
252 else if (thisJson.empty())
253 {
254 // Don't do anything for the empty objects,parse next json
255 // eg {"RemoteRoleMapping",[{}]}
256 }
257 else
258 {
259 // update/create the object
260 std::optional<std::string> remoteGroup;
261 std::optional<std::string> localRole;
262
Ed Tanousf23b7292020-10-15 09:41:17 -0700263 // This is a copy, but it's required in this case because of how
264 // readJson is structured
265 nlohmann::json thisJsonCopy = thisJson;
266 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
267 "RemoteGroup", remoteGroup, "LocalRole",
268 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530269 {
270 continue;
271 }
272
273 // Update existing RoleMapping Object
274 if (index < roleMapObjData.size())
275 {
276 BMCWEB_LOG_DEBUG << "Update Role Map Object";
277 // If "RemoteGroup" info is provided
278 if (remoteGroup)
279 {
280 crow::connections::systemBus->async_method_call(
281 [asyncResp, roleMapObjData, serverType, index,
282 remoteGroup](const boost::system::error_code ec) {
283 if (ec)
284 {
285 BMCWEB_LOG_ERROR << "DBUS response error: "
286 << ec;
287 messages::internalError(asyncResp->res);
288 return;
289 }
290 asyncResp->res
291 .jsonValue[serverType]["RemoteRoleMapping"]
292 [index]["RemoteGroup"] = *remoteGroup;
293 },
294 ldapDbusService, roleMapObjData[index].first,
295 propertyInterface, "Set",
296 "xyz.openbmc_project.User.PrivilegeMapperEntry",
297 "GroupName",
298 std::variant<std::string>(std::move(*remoteGroup)));
299 }
300
301 // If "LocalRole" info is provided
302 if (localRole)
303 {
304 crow::connections::systemBus->async_method_call(
305 [asyncResp, roleMapObjData, serverType, index,
306 localRole](const boost::system::error_code ec) {
307 if (ec)
308 {
309 BMCWEB_LOG_ERROR << "DBUS response error: "
310 << ec;
311 messages::internalError(asyncResp->res);
312 return;
313 }
314 asyncResp->res
315 .jsonValue[serverType]["RemoteRoleMapping"]
316 [index]["LocalRole"] = *localRole;
317 },
318 ldapDbusService, roleMapObjData[index].first,
319 propertyInterface, "Set",
320 "xyz.openbmc_project.User.PrivilegeMapperEntry",
321 "Privilege",
322 std::variant<std::string>(
323 getPrivilegeFromRoleId(std::move(*localRole))));
324 }
325 }
326 // Create a new RoleMapping Object.
327 else
328 {
329 BMCWEB_LOG_DEBUG
330 << "setRoleMappingProperties: Creating new Object";
331 std::string pathString =
332 "RemoteRoleMapping/" + std::to_string(index);
333
334 if (!localRole)
335 {
336 messages::propertyMissing(asyncResp->res,
337 pathString + "/LocalRole");
338 continue;
339 }
340 if (!remoteGroup)
341 {
342 messages::propertyMissing(asyncResp->res,
343 pathString + "/RemoteGroup");
344 continue;
345 }
346
347 std::string dbusObjectPath;
348 if (serverType == "ActiveDirectory")
349 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700350 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530351 }
352 else if (serverType == "LDAP")
353 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000354 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530355 }
356
357 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
358 << ",LocalRole=" << *localRole;
359
360 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700361 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530362 remoteGroup](const boost::system::error_code ec) {
363 if (ec)
364 {
365 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
366 messages::internalError(asyncResp->res);
367 return;
368 }
369 nlohmann::json& remoteRoleJson =
370 asyncResp->res
371 .jsonValue[serverType]["RemoteRoleMapping"];
372 remoteRoleJson.push_back(
373 {{"LocalRole", *localRole},
374 {"RemoteGroup", *remoteGroup}});
375 },
376 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700377 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530378 getPrivilegeFromRoleId(std::move(*localRole)));
379 }
380 }
381 }
382}
383
384/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530385 * Function that retrieves all properties for LDAP config object
386 * into JSON
387 */
388template <typename CallbackFunc>
389inline void getLDAPConfigData(const std::string& ldapType,
390 CallbackFunc&& callback)
391{
Ratan Guptaab828d72019-04-22 14:18:33 +0530392
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600393 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530394 ldapConfigInterface};
395
396 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600397 [callback, ldapType](const boost::system::error_code ec,
398 const GetObjectType& resp) {
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600399 if (ec || resp.empty())
400 {
George Liu0fda0f12021-11-16 10:06:17 +0800401 BMCWEB_LOG_ERROR
402 << "DBUS response error during getting of service name: "
403 << ec;
Ed Tanous23a21a12020-07-25 04:45:05 +0000404 LDAPConfigData empty{};
405 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600406 return;
407 }
408 std::string service = resp.begin()->first;
409 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000410 [callback, ldapType](const boost::system::error_code errorCode,
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600411 const ManagedObjectType& ldapObjects) {
412 LDAPConfigData confData{};
Ed Tanous81ce6092020-12-17 16:54:55 +0000413 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600414 {
415 callback(false, confData, ldapType);
416 BMCWEB_LOG_ERROR << "D-Bus responses error: "
Ed Tanous81ce6092020-12-17 16:54:55 +0000417 << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600418 return;
419 }
420
421 std::string ldapDbusType;
422 std::string searchString;
423
424 if (ldapType == "LDAP")
425 {
George Liu0fda0f12021-11-16 10:06:17 +0800426 ldapDbusType =
427 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600428 searchString = "openldap";
429 }
430 else if (ldapType == "ActiveDirectory")
431 {
432 ldapDbusType =
George Liu0fda0f12021-11-16 10:06:17 +0800433 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600434 searchString = "active_directory";
435 }
436 else
437 {
438 BMCWEB_LOG_ERROR
439 << "Can't get the DbusType for the given type="
440 << ldapType;
441 callback(false, confData, ldapType);
442 return;
443 }
444
445 std::string ldapEnableInterfaceStr = ldapEnableInterface;
446 std::string ldapConfigInterfaceStr = ldapConfigInterface;
447
448 for (const auto& object : ldapObjects)
449 {
450 // let's find the object whose ldap type is equal to the
451 // given type
452 if (object.first.str.find(searchString) ==
453 std::string::npos)
454 {
455 continue;
456 }
457
458 for (const auto& interface : object.second)
459 {
460 if (interface.first == ldapEnableInterfaceStr)
461 {
462 // rest of the properties are string.
463 for (const auto& property : interface.second)
464 {
465 if (property.first == "Enabled")
466 {
467 const bool* value =
468 std::get_if<bool>(&property.second);
469 if (value == nullptr)
470 {
471 continue;
472 }
473 confData.serviceEnabled = *value;
474 break;
475 }
476 }
477 }
478 else if (interface.first == ldapConfigInterfaceStr)
479 {
480
481 for (const auto& property : interface.second)
482 {
Ed Tanous271584a2019-07-09 16:24:22 -0700483 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600484 std::get_if<std::string>(
485 &property.second);
Ed Tanous271584a2019-07-09 16:24:22 -0700486 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600487 {
488 continue;
489 }
490 if (property.first == "LDAPServerURI")
491 {
Ed Tanous271584a2019-07-09 16:24:22 -0700492 confData.uri = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600493 }
494 else if (property.first == "LDAPBindDN")
495 {
Ed Tanous271584a2019-07-09 16:24:22 -0700496 confData.bindDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600497 }
498 else if (property.first == "LDAPBaseDN")
499 {
Ed Tanous271584a2019-07-09 16:24:22 -0700500 confData.baseDN = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600501 }
502 else if (property.first ==
503 "LDAPSearchScope")
504 {
Ed Tanous271584a2019-07-09 16:24:22 -0700505 confData.searchScope = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600506 }
507 else if (property.first ==
508 "GroupNameAttribute")
509 {
Ed Tanous271584a2019-07-09 16:24:22 -0700510 confData.groupAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600511 }
512 else if (property.first ==
513 "UserNameAttribute")
514 {
Ed Tanous271584a2019-07-09 16:24:22 -0700515 confData.userNameAttribute = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600516 }
517 else if (property.first == "LDAPType")
518 {
Ed Tanous271584a2019-07-09 16:24:22 -0700519 confData.serverType = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600520 }
521 }
522 }
George Liu0fda0f12021-11-16 10:06:17 +0800523 else if (
524 interface.first ==
525 "xyz.openbmc_project.User.PrivilegeMapperEntry")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600526 {
527 LDAPRoleMapData roleMapData{};
528 for (const auto& property : interface.second)
529 {
Ed Tanous271584a2019-07-09 16:24:22 -0700530 const std::string* strValue =
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600531 std::get_if<std::string>(
532 &property.second);
533
Ed Tanous271584a2019-07-09 16:24:22 -0700534 if (strValue == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600535 {
536 continue;
537 }
538
539 if (property.first == "GroupName")
540 {
Ed Tanous271584a2019-07-09 16:24:22 -0700541 roleMapData.groupName = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600542 }
543 else if (property.first == "Privilege")
544 {
Ed Tanous271584a2019-07-09 16:24:22 -0700545 roleMapData.privilege = *strValue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600546 }
547 }
548
Ed Tanous0f0353b2019-10-24 11:37:51 -0700549 confData.groupRoleList.emplace_back(
550 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600551 }
552 }
553 }
554 callback(true, confData, ldapType);
555 },
556 service, ldapRootObject, dbusObjManagerIntf,
557 "GetManagedObjects");
558 },
559 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
Ed Tanous23a21a12020-07-25 04:45:05 +0000560 ldapConfigObjectName, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530561}
562
Ed Tanous6c51eab2021-06-03 12:30:29 -0700563/**
564 * @brief parses the authentication section under the LDAP
565 * @param input JSON data
566 * @param asyncResp pointer to the JSON response
567 * @param userName userName to be filled from the given JSON.
568 * @param password password to be filled from the given JSON.
569 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700570inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700571 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
572 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700573{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700574 std::optional<std::string> authType;
575
576 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
577 authType, "Username", username, "Password",
578 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700579 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700580 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700581 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700582 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530583 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700584 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530585 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700586 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530587 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700588 messages::propertyValueNotInList(asyncResp->res, *authType,
589 "AuthenticationType");
590 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530591 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700592}
593/**
594 * @brief parses the LDAPService section under the LDAP
595 * @param input JSON data
596 * @param asyncResp pointer to the JSON response
597 * @param baseDNList baseDN to be filled from the given JSON.
598 * @param userNameAttribute userName to be filled from the given JSON.
599 * @param groupaAttribute password to be filled from the given JSON.
600 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530601
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700602inline void
603 parseLDAPServiceJson(nlohmann::json input,
604 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
605 std::optional<std::vector<std::string>>& baseDNList,
606 std::optional<std::string>& userNameAttribute,
607 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700608{
609 std::optional<nlohmann::json> searchSettings;
610
611 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
612 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530613 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700614 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530615 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700616 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530617 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700618 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530619 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700620 if (!json_util::readJson(*searchSettings, asyncResp->res,
621 "BaseDistinguishedNames", baseDNList,
622 "UsernameAttribute", userNameAttribute,
623 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530624 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700625 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530626 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700627}
628/**
629 * @brief updates the LDAP server address and updates the
630 json response with the new value.
631 * @param serviceAddressList address to be updated.
632 * @param asyncResp pointer to the JSON response
633 * @param ldapServerElementName Type of LDAP
634 server(openLDAP/ActiveDirectory)
635 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530636
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700637inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700638 const std::vector<std::string>& serviceAddressList,
639 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
640 const std::string& ldapServerElementName,
641 const std::string& ldapConfigObject)
642{
643 crow::connections::systemBus->async_method_call(
644 [asyncResp, ldapServerElementName,
645 serviceAddressList](const boost::system::error_code ec) {
646 if (ec)
647 {
648 BMCWEB_LOG_DEBUG
649 << "Error Occurred in updating the service address";
650 messages::internalError(asyncResp->res);
651 return;
652 }
653 std::vector<std::string> modifiedserviceAddressList = {
654 serviceAddressList.front()};
655 asyncResp->res
656 .jsonValue[ldapServerElementName]["ServiceAddresses"] =
657 modifiedserviceAddressList;
658 if ((serviceAddressList).size() > 1)
659 {
660 messages::propertyValueModified(asyncResp->res,
661 "ServiceAddresses",
662 serviceAddressList.front());
663 }
664 BMCWEB_LOG_DEBUG << "Updated the service address";
665 },
666 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
667 ldapConfigInterface, "LDAPServerURI",
668 std::variant<std::string>(serviceAddressList.front()));
669}
670/**
671 * @brief updates the LDAP Bind DN and updates the
672 json response with the new value.
673 * @param username name of the user which needs to be updated.
674 * @param asyncResp pointer to the JSON response
675 * @param ldapServerElementName Type of LDAP
676 server(openLDAP/ActiveDirectory)
677 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530678
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700679inline void
680 handleUserNamePatch(const std::string& username,
681 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
682 const std::string& ldapServerElementName,
683 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700684{
685 crow::connections::systemBus->async_method_call(
686 [asyncResp, username,
687 ldapServerElementName](const boost::system::error_code ec) {
688 if (ec)
689 {
690 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
691 messages::internalError(asyncResp->res);
692 return;
693 }
694 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
695 ["Username"] = username;
696 BMCWEB_LOG_DEBUG << "Updated the username";
697 },
698 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
699 ldapConfigInterface, "LDAPBindDN", std::variant<std::string>(username));
700}
701
702/**
703 * @brief updates the LDAP password
704 * @param password : ldap password which needs to be updated.
705 * @param asyncResp pointer to the JSON response
706 * @param ldapServerElementName Type of LDAP
707 * server(openLDAP/ActiveDirectory)
708 */
709
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700710inline void
711 handlePasswordPatch(const std::string& password,
712 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
713 const std::string& ldapServerElementName,
714 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700715{
716 crow::connections::systemBus->async_method_call(
717 [asyncResp, password,
718 ldapServerElementName](const boost::system::error_code ec) {
719 if (ec)
720 {
721 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
722 messages::internalError(asyncResp->res);
723 return;
724 }
725 asyncResp->res.jsonValue[ldapServerElementName]["Authentication"]
726 ["Password"] = "";
727 BMCWEB_LOG_DEBUG << "Updated the password";
728 },
729 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
730 ldapConfigInterface, "LDAPBindDNPassword",
731 std::variant<std::string>(password));
732}
733
734/**
735 * @brief updates the LDAP BaseDN and updates the
736 json response with the new value.
737 * @param baseDNList baseDN list which needs to be updated.
738 * @param asyncResp pointer to the JSON response
739 * @param ldapServerElementName Type of LDAP
740 server(openLDAP/ActiveDirectory)
741 */
742
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700743inline void
744 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
745 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
746 const std::string& ldapServerElementName,
747 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700748{
749 crow::connections::systemBus->async_method_call(
750 [asyncResp, baseDNList,
751 ldapServerElementName](const boost::system::error_code ec) {
752 if (ec)
753 {
754 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
755 messages::internalError(asyncResp->res);
756 return;
757 }
758 auto& serverTypeJson =
759 asyncResp->res.jsonValue[ldapServerElementName];
760 auto& searchSettingsJson =
761 serverTypeJson["LDAPService"]["SearchSettings"];
762 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
763 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
764 if (baseDNList.size() > 1)
765 {
766 messages::propertyValueModified(asyncResp->res,
767 "BaseDistinguishedNames",
768 baseDNList.front());
769 }
770 BMCWEB_LOG_DEBUG << "Updated the base DN";
771 },
772 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
773 ldapConfigInterface, "LDAPBaseDN",
774 std::variant<std::string>(baseDNList.front()));
775}
776/**
777 * @brief updates the LDAP user name attribute and updates the
778 json response with the new value.
779 * @param userNameAttribute attribute to be updated.
780 * @param asyncResp pointer to the JSON response
781 * @param ldapServerElementName Type of LDAP
782 server(openLDAP/ActiveDirectory)
783 */
784
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700785inline void
786 handleUserNameAttrPatch(const std::string& userNameAttribute,
787 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
788 const std::string& ldapServerElementName,
789 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700790{
791 crow::connections::systemBus->async_method_call(
792 [asyncResp, userNameAttribute,
793 ldapServerElementName](const boost::system::error_code ec) {
794 if (ec)
795 {
796 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
797 "username attribute";
798 messages::internalError(asyncResp->res);
799 return;
800 }
801 auto& serverTypeJson =
802 asyncResp->res.jsonValue[ldapServerElementName];
803 auto& searchSettingsJson =
804 serverTypeJson["LDAPService"]["SearchSettings"];
805 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
806 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
807 },
808 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
809 ldapConfigInterface, "UserNameAttribute",
810 std::variant<std::string>(userNameAttribute));
811}
812/**
813 * @brief updates the LDAP group attribute and updates the
814 json response with the new value.
815 * @param groupsAttribute attribute to be updated.
816 * @param asyncResp pointer to the JSON response
817 * @param ldapServerElementName Type of LDAP
818 server(openLDAP/ActiveDirectory)
819 */
820
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700821inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700822 const std::string& groupsAttribute,
823 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
824 const std::string& ldapServerElementName,
825 const std::string& ldapConfigObject)
826{
827 crow::connections::systemBus->async_method_call(
828 [asyncResp, groupsAttribute,
829 ldapServerElementName](const boost::system::error_code ec) {
830 if (ec)
831 {
832 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
833 "groupname attribute";
834 messages::internalError(asyncResp->res);
835 return;
836 }
837 auto& serverTypeJson =
838 asyncResp->res.jsonValue[ldapServerElementName];
839 auto& searchSettingsJson =
840 serverTypeJson["LDAPService"]["SearchSettings"];
841 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
842 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
843 },
844 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
845 ldapConfigInterface, "GroupNameAttribute",
846 std::variant<std::string>(groupsAttribute));
847}
848/**
849 * @brief updates the LDAP service enable and updates the
850 json response with the new value.
851 * @param input JSON data.
852 * @param asyncResp pointer to the JSON response
853 * @param ldapServerElementName Type of LDAP
854 server(openLDAP/ActiveDirectory)
855 */
856
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700857inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700858 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
859 const std::string& ldapServerElementName,
860 const std::string& ldapConfigObject)
861{
862 crow::connections::systemBus->async_method_call(
863 [asyncResp, serviceEnabled,
864 ldapServerElementName](const boost::system::error_code ec) {
865 if (ec)
866 {
867 BMCWEB_LOG_DEBUG
868 << "Error Occurred in Updating the service enable";
869 messages::internalError(asyncResp->res);
870 return;
871 }
872 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
873 serviceEnabled;
874 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
875 },
876 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
877 ldapEnableInterface, "Enabled", std::variant<bool>(serviceEnabled));
878}
879
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700880inline void
881 handleAuthMethodsPatch(nlohmann::json& input,
882 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700883{
884 std::optional<bool> basicAuth;
885 std::optional<bool> cookie;
886 std::optional<bool> sessionToken;
887 std::optional<bool> xToken;
888 std::optional<bool> tls;
889
890 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
891 "Cookie", cookie, "SessionToken", sessionToken,
892 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530893 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700894 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
895 return;
896 }
897
898 // Make a copy of methods configuration
899 persistent_data::AuthConfigMethods authMethodsConfig =
900 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
901
902 if (basicAuth)
903 {
904#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
905 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800906 asyncResp->res,
907 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700908 return;
909#endif
910 authMethodsConfig.basic = *basicAuth;
911 }
912
913 if (cookie)
914 {
915#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800916 messages::actionNotSupported(
917 asyncResp->res,
918 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700919 return;
920#endif
921 authMethodsConfig.cookie = *cookie;
922 }
923
924 if (sessionToken)
925 {
926#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
927 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800928 asyncResp->res,
929 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700930 return;
931#endif
932 authMethodsConfig.sessionToken = *sessionToken;
933 }
934
935 if (xToken)
936 {
937#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800938 messages::actionNotSupported(
939 asyncResp->res,
940 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700941 return;
942#endif
943 authMethodsConfig.xtoken = *xToken;
944 }
945
946 if (tls)
947 {
948#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800949 messages::actionNotSupported(
950 asyncResp->res,
951 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700952 return;
953#endif
954 authMethodsConfig.tls = *tls;
955 }
956
957 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
958 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
959 !authMethodsConfig.tls)
960 {
961 // Do not allow user to disable everything
962 messages::actionNotSupported(asyncResp->res,
963 "of disabling all available methods");
964 return;
965 }
966
967 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
968 authMethodsConfig);
969 // Save configuration immediately
970 persistent_data::getConfig().writeData();
971
972 messages::success(asyncResp->res);
973}
974
975/**
976 * @brief Get the required values from the given JSON, validates the
977 * value and create the LDAP config object.
978 * @param input JSON data
979 * @param asyncResp pointer to the JSON response
980 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
981 */
982
983inline void handleLDAPPatch(nlohmann::json& input,
984 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
985 const std::string& serverType)
986{
987 std::string dbusObjectPath;
988 if (serverType == "ActiveDirectory")
989 {
990 dbusObjectPath = adConfigObject;
991 }
992 else if (serverType == "LDAP")
993 {
994 dbusObjectPath = ldapConfigObjectName;
995 }
996 else
997 {
998 return;
999 }
1000
1001 std::optional<nlohmann::json> authentication;
1002 std::optional<nlohmann::json> ldapService;
1003 std::optional<std::vector<std::string>> serviceAddressList;
1004 std::optional<bool> serviceEnabled;
1005 std::optional<std::vector<std::string>> baseDNList;
1006 std::optional<std::string> userNameAttribute;
1007 std::optional<std::string> groupsAttribute;
1008 std::optional<std::string> userName;
1009 std::optional<std::string> password;
1010 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
1011
1012 if (!json_util::readJson(input, asyncResp->res, "Authentication",
1013 authentication, "LDAPService", ldapService,
1014 "ServiceAddresses", serviceAddressList,
1015 "ServiceEnabled", serviceEnabled,
1016 "RemoteRoleMapping", remoteRoleMapData))
1017 {
1018 return;
1019 }
1020
1021 if (authentication)
1022 {
1023 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
1024 password);
1025 }
1026 if (ldapService)
1027 {
1028 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1029 userNameAttribute, groupsAttribute);
1030 }
1031 if (serviceAddressList)
1032 {
1033 if ((*serviceAddressList).size() == 0)
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301034 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001035 messages::propertyValueNotInList(asyncResp->res, "[]",
1036 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001037 return;
1038 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001039 }
1040 if (baseDNList)
1041 {
1042 if ((*baseDNList).size() == 0)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301043 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001044 messages::propertyValueNotInList(asyncResp->res, "[]",
1045 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301046 return;
1047 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001048 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301049
Ed Tanous6c51eab2021-06-03 12:30:29 -07001050 // nothing to update, then return
1051 if (!userName && !password && !serviceAddressList && !baseDNList &&
1052 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1053 !remoteRoleMapData)
1054 {
1055 return;
1056 }
1057
1058 // Get the existing resource first then keep modifying
1059 // whenever any property gets updated.
1060 getLDAPConfigData(serverType, [asyncResp, userName, password, baseDNList,
1061 userNameAttribute, groupsAttribute,
1062 serviceAddressList, serviceEnabled,
1063 dbusObjectPath, remoteRoleMapData](
1064 bool success,
1065 const LDAPConfigData& confData,
1066 const std::string& serverT) {
1067 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301068 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001069 messages::internalError(asyncResp->res);
1070 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301071 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001072 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1073 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301074 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001075 // Disable the service first and update the rest of
1076 // the properties.
1077 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301078 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001079
Ratan Gupta8a07d282019-03-16 08:33:47 +05301080 if (serviceAddressList)
1081 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001082 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1083 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301084 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001085 if (userName)
1086 {
1087 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1088 }
1089 if (password)
1090 {
1091 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1092 }
1093
Ratan Gupta8a07d282019-03-16 08:33:47 +05301094 if (baseDNList)
1095 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001096 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1097 }
1098 if (userNameAttribute)
1099 {
1100 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1101 dbusObjectPath);
1102 }
1103 if (groupsAttribute)
1104 {
1105 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1106 dbusObjectPath);
1107 }
1108 if (serviceEnabled)
1109 {
1110 // if user has given the value as true then enable
1111 // the service. if user has given false then no-op
1112 // as service is already stopped.
1113 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301114 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001115 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1116 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301117 }
1118 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001119 else
1120 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001121 // if user has not given the service enabled value
1122 // then revert it to the same state as it was
1123 // before.
1124 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1125 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001126 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001127
Ed Tanous6c51eab2021-06-03 12:30:29 -07001128 if (remoteRoleMapData)
1129 {
1130 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1131 *remoteRoleMapData);
1132 }
1133 });
1134}
1135
1136inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1137 const std::string& username,
1138 std::optional<std::string> password,
1139 std::optional<bool> enabled,
1140 std::optional<std::string> roleId,
1141 std::optional<bool> locked)
1142{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301143 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1144 tempObjPath /= username;
1145 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001146
1147 dbus::utility::checkDbusPathExists(
1148 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001149 [dbusObjectPath, username, password(std::move(password)),
1150 roleId(std::move(roleId)), enabled, locked,
1151 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001152 if (!rc)
1153 {
1154 messages::resourceNotFound(
1155 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1156 username);
1157 return;
1158 }
1159
1160 if (password)
1161 {
1162 int retval = pamUpdatePassword(username, *password);
1163
1164 if (retval == PAM_USER_UNKNOWN)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001165 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001166 messages::resourceNotFound(
1167 asyncResp->res, "#ManagerAccount.v1_4_0.ManagerAccount",
1168 username);
1169 }
1170 else if (retval == PAM_AUTHTOK_ERR)
1171 {
1172 // If password is invalid
1173 messages::propertyValueFormatError(asyncResp->res,
1174 *password, "Password");
1175 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1176 }
1177 else if (retval != PAM_SUCCESS)
1178 {
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001179 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001180 return;
1181 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001182 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001183
Ed Tanous6c51eab2021-06-03 12:30:29 -07001184 if (enabled)
1185 {
1186 crow::connections::systemBus->async_method_call(
1187 [asyncResp](const boost::system::error_code ec) {
1188 if (ec)
1189 {
1190 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1191 messages::internalError(asyncResp->res);
1192 return;
1193 }
1194 messages::success(asyncResp->res);
1195 return;
1196 },
1197 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1198 "org.freedesktop.DBus.Properties", "Set",
1199 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1200 std::variant<bool>{*enabled});
1201 }
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001202
Ed Tanous6c51eab2021-06-03 12:30:29 -07001203 if (roleId)
1204 {
1205 std::string priv = getPrivilegeFromRoleId(*roleId);
1206 if (priv.empty())
Ed Tanous04ae99e2018-09-20 15:54:36 -07001207 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001208 messages::propertyValueNotInList(asyncResp->res, *roleId,
1209 "RoleId");
1210 return;
1211 }
1212 if (priv == "priv-noaccess")
1213 {
1214 priv = "";
1215 }
1216
1217 crow::connections::systemBus->async_method_call(
1218 [asyncResp](const boost::system::error_code ec) {
1219 if (ec)
1220 {
1221 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1222 messages::internalError(asyncResp->res);
1223 return;
1224 }
1225 messages::success(asyncResp->res);
1226 },
1227 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1228 "org.freedesktop.DBus.Properties", "Set",
1229 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1230 std::variant<std::string>{priv});
1231 }
1232
1233 if (locked)
1234 {
1235 // admin can unlock the account which is locked by
1236 // successive authentication failures but admin should
1237 // not be allowed to lock an account.
1238 if (*locked)
1239 {
1240 messages::propertyValueNotInList(asyncResp->res, "true",
1241 "Locked");
Ed Tanous04ae99e2018-09-20 15:54:36 -07001242 return;
1243 }
1244
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001245 crow::connections::systemBus->async_method_call(
Ed Tanous6c51eab2021-06-03 12:30:29 -07001246 [asyncResp](const boost::system::error_code ec) {
1247 if (ec)
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001248 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001249 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1250 messages::internalError(asyncResp->res);
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001251 return;
1252 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001253 messages::success(asyncResp->res);
1254 return;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001255 },
Ed Tanous6c51eab2021-06-03 12:30:29 -07001256 "xyz.openbmc_project.User.Manager", dbusObjectPath.c_str(),
1257 "org.freedesktop.DBus.Properties", "Set",
1258 "xyz.openbmc_project.User.Attributes",
1259 "UserLockedForFailedAttempt", std::variant<bool>{*locked});
1260 }
1261 });
1262}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001263
Ed Tanous6c51eab2021-06-03 12:30:29 -07001264inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001265{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001266
Ed Tanous6c51eab2021-06-03 12:30:29 -07001267 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001268 .privileges(redfish::privileges::getAccountService)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001269 .methods(
Abhishek Patel72048782021-06-02 09:53:24 -05001270 boost::beast::http::verb::get)([](const crow::Request& req,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001271 const std::shared_ptr<
1272 bmcweb::AsyncResp>& asyncResp)
1273 -> void {
1274 const persistent_data::AuthConfigMethods& authMethodsConfig =
1275 persistent_data::SessionStore::getInstance()
1276 .getAuthMethodsConfig();
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001277
Ed Tanous6c51eab2021-06-03 12:30:29 -07001278 asyncResp->res.jsonValue = {
1279 {"@odata.id", "/redfish/v1/AccountService"},
1280 {"@odata.type", "#AccountService."
1281 "v1_5_0.AccountService"},
1282 {"Id", "AccountService"},
1283 {"Name", "Account Service"},
1284 {"Description", "Account Service"},
1285 {"ServiceEnabled", true},
1286 {"MaxPasswordLength", 20},
1287 {"Accounts",
1288 {{"@odata.id", "/redfish/v1/AccountService/Accounts"}}},
1289 {"Roles", {{"@odata.id", "/redfish/v1/AccountService/Roles"}}},
1290 {"Oem",
1291 {{"OpenBMC",
1292 {{"@odata.type", "#OemAccountService.v1_0_0.AccountService"},
Ed Tanousd8e3c292021-09-21 18:01:43 -07001293 {"@odata.id", "/redfish/v1/AccountService#/Oem/OpenBMC"},
Ed Tanous6c51eab2021-06-03 12:30:29 -07001294 {"AuthMethods",
1295 {
1296 {"BasicAuth", authMethodsConfig.basic},
1297 {"SessionToken", authMethodsConfig.sessionToken},
1298 {"XToken", authMethodsConfig.xtoken},
1299 {"Cookie", authMethodsConfig.cookie},
1300 {"TLS", authMethodsConfig.tls},
Abhishek Patel72048782021-06-02 09:53:24 -05001301 }}}}}}};
1302 // /redfish/v1/AccountService/LDAP/Certificates is something only
1303 // ConfigureManager can access then only display when the user has
1304 // permissions ConfigureManager
1305 Privileges effectiveUserPrivileges =
1306 redfish::getUserPrivileges(req.userRole);
1307
1308 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1309 effectiveUserPrivileges))
1310 {
1311 asyncResp->res.jsonValue["LDAP"] = {
1312 {"Certificates",
1313 {{"@odata.id",
1314 "/redfish/v1/AccountService/LDAP/Certificates"}}}};
1315 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001316 crow::connections::systemBus->async_method_call(
1317 [asyncResp](
1318 const boost::system::error_code ec,
1319 const std::vector<
1320 std::pair<std::string,
1321 std::variant<uint32_t, uint16_t, uint8_t>>>&
1322 propertiesList) {
1323 if (ec)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +00001324 {
1325 messages::internalError(asyncResp->res);
1326 return;
1327 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001328 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1329 << "properties for AccountService";
1330 for (const std::pair<std::string,
1331 std::variant<uint32_t, uint16_t,
1332 uint8_t>>& property :
1333 propertiesList)
1334 {
1335 if (property.first == "MinPasswordLength")
1336 {
1337 const uint8_t* value =
1338 std::get_if<uint8_t>(&property.second);
1339 if (value != nullptr)
Ratan Gupta24c85422019-01-30 19:41:24 +05301340 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001341 asyncResp->res.jsonValue["MinPasswordLength"] =
1342 *value;
Ratan Gupta24c85422019-01-30 19:41:24 +05301343 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001344 }
1345 if (property.first == "AccountUnlockTimeout")
1346 {
1347 const uint32_t* value =
1348 std::get_if<uint32_t>(&property.second);
1349 if (value != nullptr)
1350 {
1351 asyncResp->res
1352 .jsonValue["AccountLockoutDuration"] =
1353 *value;
1354 }
1355 }
1356 if (property.first == "MaxLoginAttemptBeforeLockout")
1357 {
1358 const uint16_t* value =
1359 std::get_if<uint16_t>(&property.second);
1360 if (value != nullptr)
1361 {
1362 asyncResp->res
1363 .jsonValue["AccountLockoutThreshold"] =
1364 *value;
1365 }
1366 }
1367 }
1368 },
1369 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1370 "org.freedesktop.DBus.Properties", "GetAll",
1371 "xyz.openbmc_project.User.AccountPolicy");
1372
1373 auto callback = [asyncResp](bool success, LDAPConfigData& confData,
1374 const std::string& ldapType) {
1375 if (!success)
1376 {
1377 return;
1378 }
1379 parseLDAPConfigData(asyncResp->res.jsonValue, confData,
1380 ldapType);
1381 };
1382
1383 getLDAPConfigData("LDAP", callback);
1384 getLDAPConfigData("ActiveDirectory", callback);
1385 });
1386
Ed Tanousf5ffd802021-07-19 10:55:33 -07001387 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
1388 .privileges(redfish::privileges::getAccountService)
1389 .methods(boost::beast::http::verb::patch)(
1390 [](const crow::Request& req,
1391 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1392 std::optional<uint32_t> unlockTimeout;
1393 std::optional<uint16_t> lockoutThreshold;
1394 std::optional<uint16_t> minPasswordLength;
1395 std::optional<uint16_t> maxPasswordLength;
1396 std::optional<nlohmann::json> ldapObject;
1397 std::optional<nlohmann::json> activeDirectoryObject;
1398 std::optional<nlohmann::json> oemObject;
1399
1400 if (!json_util::readJson(
1401 req, asyncResp->res, "AccountLockoutDuration",
1402 unlockTimeout, "AccountLockoutThreshold",
1403 lockoutThreshold, "MaxPasswordLength",
1404 maxPasswordLength, "MinPasswordLength",
1405 minPasswordLength, "LDAP", ldapObject,
1406 "ActiveDirectory", activeDirectoryObject, "Oem",
1407 oemObject))
1408 {
1409 return;
1410 }
1411
1412 if (minPasswordLength)
1413 {
1414 messages::propertyNotWritable(asyncResp->res,
1415 "MinPasswordLength");
1416 }
1417
1418 if (maxPasswordLength)
1419 {
1420 messages::propertyNotWritable(asyncResp->res,
1421 "MaxPasswordLength");
1422 }
1423
1424 if (ldapObject)
1425 {
1426 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1427 }
1428
1429 if (std::optional<nlohmann::json> oemOpenBMCObject;
1430 oemObject &&
1431 json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1432 oemOpenBMCObject))
1433 {
1434 if (std::optional<nlohmann::json> authMethodsObject;
1435 oemOpenBMCObject &&
1436 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1437 "AuthMethods", authMethodsObject))
1438 {
1439 if (authMethodsObject)
1440 {
1441 handleAuthMethodsPatch(*authMethodsObject,
1442 asyncResp);
1443 }
1444 }
1445 }
1446
1447 if (activeDirectoryObject)
1448 {
1449 handleLDAPPatch(*activeDirectoryObject, asyncResp,
1450 "ActiveDirectory");
1451 }
1452
1453 if (unlockTimeout)
1454 {
1455 crow::connections::systemBus->async_method_call(
1456 [asyncResp](const boost::system::error_code ec) {
1457 if (ec)
1458 {
1459 messages::internalError(asyncResp->res);
1460 return;
1461 }
1462 messages::success(asyncResp->res);
1463 },
1464 "xyz.openbmc_project.User.Manager",
1465 "/xyz/openbmc_project/user",
1466 "org.freedesktop.DBus.Properties", "Set",
1467 "xyz.openbmc_project.User.AccountPolicy",
1468 "AccountUnlockTimeout",
1469 std::variant<uint32_t>(*unlockTimeout));
1470 }
1471 if (lockoutThreshold)
1472 {
1473 crow::connections::systemBus->async_method_call(
1474 [asyncResp](const boost::system::error_code ec) {
1475 if (ec)
1476 {
1477 messages::internalError(asyncResp->res);
1478 return;
1479 }
1480 messages::success(asyncResp->res);
1481 },
1482 "xyz.openbmc_project.User.Manager",
1483 "/xyz/openbmc_project/user",
1484 "org.freedesktop.DBus.Properties", "Set",
1485 "xyz.openbmc_project.User.AccountPolicy",
1486 "MaxLoginAttemptBeforeLockout",
1487 std::variant<uint16_t>(*lockoutThreshold));
1488 }
1489 });
1490
Ed Tanous6c51eab2021-06-03 12:30:29 -07001491 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001492 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001493 .methods(boost::beast::http::verb::get)(
1494 [](const crow::Request& req,
1495 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
1496 asyncResp->res.jsonValue = {
1497 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
1498 {"@odata.type", "#ManagerAccountCollection."
1499 "ManagerAccountCollection"},
1500 {"Name", "Accounts Collection"},
1501 {"Description", "BMC User Accounts"}};
1502
Ed Tanous6c51eab2021-06-03 12:30:29 -07001503 Privileges effectiveUserPrivileges =
1504 redfish::getUserPrivileges(req.userRole);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001505
JunLin Chenf5e29f32021-12-08 16:47:04 +08001506 std::string thisUser;
1507 if (req.session)
1508 {
1509 thisUser = req.session->username;
1510 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001511 crow::connections::systemBus->async_method_call(
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001512 [asyncResp, thisUser, effectiveUserPrivileges](
1513 const boost::system::error_code ec,
1514 const ManagedObjectType& users) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001515 if (ec)
1516 {
1517 messages::internalError(asyncResp->res);
Ratan Gupta24c85422019-01-30 19:41:24 +05301518 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001519 }
Ed Tanous9712f8a2018-09-21 13:38:49 -07001520
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001521 bool userCanSeeAllAccounts =
1522 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001523 {"ConfigureUsers"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001524
1525 bool userCanSeeSelf =
1526 effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001527 {"ConfigureSelf"});
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001528
Ed Tanous6c51eab2021-06-03 12:30:29 -07001529 nlohmann::json& memberArray =
1530 asyncResp->res.jsonValue["Members"];
1531 memberArray = nlohmann::json::array();
Ratan Gupta24c85422019-01-30 19:41:24 +05301532
Ed Tanous6c51eab2021-06-03 12:30:29 -07001533 for (auto& userpath : users)
1534 {
1535 std::string user = userpath.first.filename();
1536 if (user.empty())
Ratan Gupta24c85422019-01-30 19:41:24 +05301537 {
Ratan Gupta24c85422019-01-30 19:41:24 +05301538 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001539 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1540
Ratan Gupta24c85422019-01-30 19:41:24 +05301541 return;
1542 }
Ratan Gupta24c85422019-01-30 19:41:24 +05301543
Ed Tanous6c51eab2021-06-03 12:30:29 -07001544 // As clarified by Redfish here:
1545 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1546 // Users without ConfigureUsers, only see their own
1547 // account. Users with ConfigureUsers, see all
1548 // accounts.
Ed Tanouscef1ddf2021-06-03 13:45:10 -07001549 if (userCanSeeAllAccounts ||
1550 (thisUser == user && userCanSeeSelf))
Ratan Gupta24c85422019-01-30 19:41:24 +05301551 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001552 memberArray.push_back(
1553 {{"@odata.id",
1554 "/redfish/v1/AccountService/Accounts/" +
1555 user}});
Ratan Gupta24c85422019-01-30 19:41:24 +05301556 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001557 }
1558 asyncResp->res.jsonValue["Members@odata.count"] =
1559 memberArray.size();
1560 },
1561 "xyz.openbmc_project.User.Manager",
1562 "/xyz/openbmc_project/user",
1563 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Ratan Gupta24c85422019-01-30 19:41:24 +05301564 });
Ed Tanous06e086d2018-09-19 17:19:52 -07001565
Ed Tanous6c51eab2021-06-03 12:30:29 -07001566 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001567 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001568 .methods(boost::beast::http::verb::post)([](const crow::Request& req,
1569 const std::shared_ptr<
1570 bmcweb::AsyncResp>&
1571 asyncResp) -> void {
1572 std::string username;
1573 std::string password;
1574 std::optional<std::string> roleId("User");
1575 std::optional<bool> enabled = true;
1576 if (!json_util::readJson(req, asyncResp->res, "UserName", username,
1577 "Password", password, "RoleId", roleId,
1578 "Enabled", enabled))
1579 {
1580 return;
1581 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001582
Ed Tanous6c51eab2021-06-03 12:30:29 -07001583 std::string priv = getPrivilegeFromRoleId(*roleId);
1584 if (priv.empty())
1585 {
1586 messages::propertyValueNotInList(asyncResp->res, *roleId,
1587 "RoleId");
1588 return;
1589 }
1590 // TODO: Following override will be reverted once support in
1591 // phosphor-user-manager is added. In order to avoid dependency
1592 // issues, this is added in bmcweb, which will removed, once
1593 // phosphor-user-manager supports priv-noaccess.
1594 if (priv == "priv-noaccess")
1595 {
1596 roleId = "";
1597 }
1598 else
1599 {
1600 roleId = priv;
1601 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001602
Ed Tanous6c51eab2021-06-03 12:30:29 -07001603 // Reading AllGroups property
1604 crow::connections::systemBus->async_method_call(
1605 [asyncResp, username, password{std::move(password)}, roleId,
1606 enabled](
1607 const boost::system::error_code ec,
1608 const std::variant<std::vector<std::string>>& allGroups) {
1609 if (ec)
1610 {
1611 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1612 messages::internalError(asyncResp->res);
1613 return;
1614 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001615
Ed Tanous6c51eab2021-06-03 12:30:29 -07001616 const std::vector<std::string>* allGroupsList =
1617 std::get_if<std::vector<std::string>>(&allGroups);
1618
1619 if (allGroupsList == nullptr || allGroupsList->empty())
1620 {
1621 messages::internalError(asyncResp->res);
1622 return;
1623 }
1624
1625 crow::connections::systemBus->async_method_call(
1626 [asyncResp, username,
1627 password](const boost::system::error_code ec2,
1628 sdbusplus::message::message& m) {
1629 if (ec2)
1630 {
1631 userErrorMessageHandler(
1632 m.get_error(), asyncResp, username, "");
1633 return;
1634 }
1635
1636 if (pamUpdatePassword(username, password) !=
1637 PAM_SUCCESS)
1638 {
1639 // At this point we have a user that's been
1640 // created, but the password set
1641 // failed.Something is wrong, so delete the user
1642 // that we've already created
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301643 sdbusplus::message::object_path tempObjPath(
1644 rootUserDbusPath);
1645 tempObjPath /= username;
1646 const std::string userPath(tempObjPath);
1647
Ed Tanous6c51eab2021-06-03 12:30:29 -07001648 crow::connections::systemBus->async_method_call(
1649 [asyncResp, password](
1650 const boost::system::error_code ec3) {
1651 if (ec3)
1652 {
1653 messages::internalError(
1654 asyncResp->res);
1655 return;
1656 }
1657
1658 // If password is invalid
1659 messages::propertyValueFormatError(
1660 asyncResp->res, password,
1661 "Password");
1662 },
1663 "xyz.openbmc_project.User.Manager",
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301664 userPath,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001665 "xyz.openbmc_project.Object.Delete",
1666 "Delete");
1667
1668 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1669 return;
1670 }
1671
1672 messages::created(asyncResp->res);
1673 asyncResp->res.addHeader(
1674 "Location",
1675 "/redfish/v1/AccountService/Accounts/" +
1676 username);
1677 },
1678 "xyz.openbmc_project.User.Manager",
1679 "/xyz/openbmc_project/user",
1680 "xyz.openbmc_project.User.Manager", "CreateUser",
1681 username, *allGroupsList, *roleId, *enabled);
1682 },
1683 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1684 "org.freedesktop.DBus.Properties", "Get",
1685 "xyz.openbmc_project.User.Manager", "AllGroups");
1686 });
1687
1688 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001689 .privileges(redfish::privileges::getManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001690 .methods(
1691 boost::beast::http::verb::
1692 get)([](const crow::Request& req,
1693 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1694 const std::string& accountName) -> void {
1695 if (req.session->username != accountName)
1696 {
1697 // At this point we've determined that the user is trying to
1698 // modify a user that isn't them. We need to verify that they
1699 // have permissions to modify other users, so re-run the auth
1700 // check with the same permissions, minus ConfigureSelf.
1701 Privileges effectiveUserPrivileges =
1702 redfish::getUserPrivileges(req.userRole);
1703 Privileges requiredPermissionsToChangeNonSelf = {
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001704 "ConfigureUsers", "ConfigureManager"};
Ed Tanous6c51eab2021-06-03 12:30:29 -07001705 if (!effectiveUserPrivileges.isSupersetOf(
1706 requiredPermissionsToChangeNonSelf))
Ed Tanous06e086d2018-09-19 17:19:52 -07001707 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001708 BMCWEB_LOG_DEBUG << "GET Account denied access";
1709 messages::insufficientPrivilege(asyncResp->res);
1710 return;
1711 }
1712 }
1713
1714 crow::connections::systemBus->async_method_call(
1715 [asyncResp, accountName](const boost::system::error_code ec,
1716 const ManagedObjectType& users) {
1717 if (ec)
1718 {
1719 messages::internalError(asyncResp->res);
1720 return;
1721 }
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301722 auto userIt = std::find_if(
1723 users.begin(), users.end(),
1724 [accountName](
1725 const std::pair<sdbusplus::message::object_path,
1726 DbusInterfaceType>& user) {
1727 return !accountName.compare(user.first.filename());
1728 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001729
Ed Tanous6c51eab2021-06-03 12:30:29 -07001730 if (userIt == users.end())
1731 {
1732 messages::resourceNotFound(
1733 asyncResp->res, "ManagerAccount", accountName);
1734 return;
1735 }
1736
1737 asyncResp->res.jsonValue = {
1738 {"@odata.type",
1739 "#ManagerAccount.v1_4_0.ManagerAccount"},
1740 {"Name", "User Account"},
1741 {"Description", "User Account"},
1742 {"Password", nullptr},
1743 {"AccountTypes", {"Redfish"}}};
1744
1745 for (const auto& interface : userIt->second)
1746 {
1747 if (interface.first ==
1748 "xyz.openbmc_project.User.Attributes")
1749 {
1750 for (const auto& property : interface.second)
1751 {
1752 if (property.first == "UserEnabled")
1753 {
1754 const bool* userEnabled =
1755 std::get_if<bool>(&property.second);
1756 if (userEnabled == nullptr)
1757 {
1758 BMCWEB_LOG_ERROR
1759 << "UserEnabled wasn't a bool";
1760 messages::internalError(asyncResp->res);
1761 return;
1762 }
1763 asyncResp->res.jsonValue["Enabled"] =
1764 *userEnabled;
1765 }
1766 else if (property.first ==
1767 "UserLockedForFailedAttempt")
1768 {
1769 const bool* userLocked =
1770 std::get_if<bool>(&property.second);
1771 if (userLocked == nullptr)
1772 {
1773 BMCWEB_LOG_ERROR << "UserLockedForF"
1774 "ailedAttempt "
1775 "wasn't a bool";
1776 messages::internalError(asyncResp->res);
1777 return;
1778 }
1779 asyncResp->res.jsonValue["Locked"] =
1780 *userLocked;
1781 asyncResp->res.jsonValue
1782 ["Locked@Redfish.AllowableValues"] = {
1783 "false"}; // can only unlock accounts
1784 }
1785 else if (property.first == "UserPrivilege")
1786 {
1787 const std::string* userPrivPtr =
1788 std::get_if<std::string>(
1789 &property.second);
1790 if (userPrivPtr == nullptr)
1791 {
1792 BMCWEB_LOG_ERROR
1793 << "UserPrivilege wasn't a "
1794 "string";
1795 messages::internalError(asyncResp->res);
1796 return;
1797 }
1798 std::string role =
1799 getRoleIdFromPrivilege(*userPrivPtr);
1800 if (role.empty())
1801 {
1802 BMCWEB_LOG_ERROR << "Invalid user role";
1803 messages::internalError(asyncResp->res);
1804 return;
1805 }
1806 asyncResp->res.jsonValue["RoleId"] = role;
1807
1808 asyncResp->res.jsonValue["Links"]["Role"] =
1809 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +08001810 "/redfish/v1/AccountService/Roles/" +
Ed Tanous6c51eab2021-06-03 12:30:29 -07001811 role}};
1812 }
1813 else if (property.first ==
1814 "UserPasswordExpired")
1815 {
1816 const bool* userPasswordExpired =
1817 std::get_if<bool>(&property.second);
1818 if (userPasswordExpired == nullptr)
1819 {
George Liu0fda0f12021-11-16 10:06:17 +08001820 BMCWEB_LOG_ERROR
1821 << "UserPasswordExpired wasn't a bool";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001822 messages::internalError(asyncResp->res);
1823 return;
1824 }
1825 asyncResp->res
1826 .jsonValue["PasswordChangeRequired"] =
1827 *userPasswordExpired;
1828 }
1829 }
1830 }
1831 }
1832
1833 asyncResp->res.jsonValue["@odata.id"] =
1834 "/redfish/v1/AccountService/Accounts/" + accountName;
1835 asyncResp->res.jsonValue["Id"] = accountName;
1836 asyncResp->res.jsonValue["UserName"] = accountName;
1837 },
1838 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1839 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1840 });
1841
1842 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001843 // TODO this privilege should be using the generated endpoints, but
1844 // because of the special handling of ConfigureSelf, it's not able to
1845 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001846 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1847 .methods(boost::beast::http::verb::patch)(
1848 [](const crow::Request& req,
1849 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1850 const std::string& username) -> void {
1851 std::optional<std::string> newUserName;
1852 std::optional<std::string> password;
1853 std::optional<bool> enabled;
1854 std::optional<std::string> roleId;
1855 std::optional<bool> locked;
Ed Tanouse9cc5172021-11-03 14:13:19 +08001856
1857 Privileges effectiveUserPrivileges =
1858 redfish::getUserPrivileges(req.userRole);
1859 Privileges configureUsers = {"ConfigureUsers"};
1860 bool userHasConfigureUsers =
1861 effectiveUserPrivileges.isSupersetOf(configureUsers);
1862 if (userHasConfigureUsers)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001863 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001864 // Users with ConfigureUsers can modify for all users
1865 if (!json_util::readJson(req, asyncResp->res, "UserName",
1866 newUserName, "Password", password,
1867 "RoleId", roleId, "Enabled",
1868 enabled, "Locked", locked))
1869 {
1870 return;
1871 }
Ed Tanous06e086d2018-09-19 17:19:52 -07001872 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001873 else
Ed Tanous6c51eab2021-06-03 12:30:29 -07001874 {
Ed Tanouse9cc5172021-11-03 14:13:19 +08001875 // ConfigureSelf accounts can only modify their own account
1876 if (username != req.session->username)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001877 {
1878 messages::insufficientPrivilege(asyncResp->res);
1879 return;
1880 }
Ed Tanouse9cc5172021-11-03 14:13:19 +08001881 // ConfigureSelf accounts can only modify their password
1882 if (!json_util::readJson(req, asyncResp->res, "Password",
1883 password))
1884 {
1885 return;
1886 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001887 }
1888
1889 // if user name is not provided in the patch method or if it
1890 // matches the user name in the URI, then we are treating it as
1891 // updating user properties other then username. If username
1892 // provided doesn't match the URI, then we are treating this as
1893 // user rename request.
1894 if (!newUserName || (newUserName.value() == username))
1895 {
1896 updateUserProperties(asyncResp, username, password, enabled,
1897 roleId, locked);
1898 return;
1899 }
1900 crow::connections::systemBus->async_method_call(
1901 [asyncResp, username, password(std::move(password)),
1902 roleId(std::move(roleId)), enabled,
1903 newUser{std::string(*newUserName)},
1904 locked](const boost::system::error_code ec,
1905 sdbusplus::message::message& m) {
1906 if (ec)
1907 {
1908 userErrorMessageHandler(m.get_error(), asyncResp,
1909 newUser, username);
1910 return;
1911 }
1912
1913 updateUserProperties(asyncResp, newUser, password,
1914 enabled, roleId, locked);
1915 },
1916 "xyz.openbmc_project.User.Manager",
1917 "/xyz/openbmc_project/user",
1918 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1919 *newUserName);
1920 });
1921
1922 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001923 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001924 .methods(boost::beast::http::verb::delete_)(
1925 [](const crow::Request& /*req*/,
1926 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1927 const std::string& username) -> void {
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301928 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1929 tempObjPath /= username;
1930 const std::string userPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001931
1932 crow::connections::systemBus->async_method_call(
1933 [asyncResp, username](const boost::system::error_code ec) {
1934 if (ec)
1935 {
1936 messages::resourceNotFound(
1937 asyncResp->res,
1938 "#ManagerAccount.v1_4_0.ManagerAccount",
1939 username);
1940 return;
1941 }
1942
1943 messages::accountRemoved(asyncResp->res);
1944 },
1945 "xyz.openbmc_project.User.Manager", userPath,
1946 "xyz.openbmc_project.Object.Delete", "Delete");
1947 });
1948}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001949
Ed Tanous1abe55e2018-09-05 08:30:59 -07001950} // namespace redfish