blob: 5d8bb15f6cda91334b42be0c0a4c822726b74c32 [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 Tanous45ca1b82022-03-25 13:07:27 -070023#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070024#include <registries/privilege_registry.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070025#include <sdbusplus/asio/property.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020026#include <sdbusplus/unpack_properties.hpp>
27#include <utils/dbus_utils.hpp>
Ed Tanousa8408792018-09-05 16:08:38 -070028#include <utils/json_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050029
Ed Tanous1abe55e2018-09-05 08:30:59 -070030namespace redfish
31{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010032
Ed Tanous23a21a12020-07-25 04:45:05 +000033constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053034 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070035constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053036 "/xyz/openbmc_project/user/ldap/active_directory";
37
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053038constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053039constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
40constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
41constexpr const char* ldapConfigInterface =
42 "xyz.openbmc_project.User.Ldap.Config";
43constexpr const char* ldapCreateInterface =
44 "xyz.openbmc_project.User.Ldap.Create";
45constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053046constexpr const char* ldapPrivMapperInterface =
47 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053048constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
49constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
50constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
51constexpr const char* mapperObjectPath = "/xyz/openbmc_project/object_mapper";
52constexpr const char* mapperIntf = "xyz.openbmc_project.ObjectMapper";
53
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060054struct LDAPRoleMapData
55{
56 std::string groupName;
57 std::string privilege;
58};
59
Ratan Gupta6973a582018-12-13 18:25:44 +053060struct LDAPConfigData
61{
62 std::string uri{};
63 std::string bindDN{};
64 std::string baseDN{};
65 std::string searchScope{};
66 std::string serverType{};
67 bool serviceEnabled = false;
68 std::string userNameAttribute{};
69 std::string groupAttribute{};
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060070 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053071};
72
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060073inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053074{
75 if (role == "priv-admin")
76 {
77 return "Administrator";
78 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070079 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053080 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053081 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053082 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070083 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053084 {
85 return "Operator";
86 }
Ed Tanous26f69762022-01-25 09:49:11 -080087 if (role.empty() || (role == "priv-noaccess"))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +000088 {
89 return "NoAccess";
90 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +053091 return "";
92}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060093inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053094{
95 if (role == "Administrator")
96 {
97 return "priv-admin";
98 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070099 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530100 {
101 return "priv-user";
102 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700103 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530104 {
105 return "priv-operator";
106 }
Ed Tanous26f69762022-01-25 09:49:11 -0800107 if ((role == "NoAccess") || (role.empty()))
jayaprakash Mutyalae9e6d242019-07-29 11:59:08 +0000108 {
109 return "priv-noaccess";
110 }
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530111 return "";
112}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700113
zhanghch058d1b46d2021-04-01 11:18:24 +0800114inline void userErrorMessageHandler(
115 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
116 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000117{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000118 if (e == nullptr)
119 {
120 messages::internalError(asyncResp->res);
121 return;
122 }
123
Manojkiran Eda055806b2020-11-03 09:36:28 +0530124 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000125 if (strcmp(errorMessage,
126 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
127 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800128 messages::resourceAlreadyExists(asyncResp->res, "ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000129 "UserName", newUser);
130 }
131 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
132 "UserNameDoesNotExist") == 0)
133 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800134 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000135 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700136 else if ((strcmp(errorMessage,
137 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
138 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800139 (strcmp(
140 errorMessage,
141 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
142 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000143 {
144 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
145 }
146 else if (strcmp(errorMessage,
147 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
148 {
149 messages::createLimitReachedForResource(asyncResp->res);
150 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000151 else
152 {
153 messages::internalError(asyncResp->res);
154 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000155}
156
Ed Tanous81ce6092020-12-17 16:54:55 +0000157inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000158 const LDAPConfigData& confData,
159 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530160{
Ratan Guptaab828d72019-04-22 14:18:33 +0530161 std::string service =
162 (ldapType == "LDAP") ? "LDAPService" : "ActiveDirectoryService";
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600163
Ed Tanous14766872022-03-15 10:44:42 -0700164 nlohmann::json& ldap = jsonResponse[ldapType];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600165
Ed Tanous14766872022-03-15 10:44:42 -0700166 ldap["ServiceEnabled"] = confData.serviceEnabled;
167 ldap["ServiceAddresses"] = nlohmann::json::array({confData.uri});
168 ldap["Authentication"]["AuthenticationType"] = "UsernameAndPassword";
169 ldap["Authentication"]["Username"] = confData.bindDN;
170 ldap["Authentication"]["Password"] = nullptr;
171
172 ldap["LDAPService"]["SearchSettings"]["BaseDistinguishedNames"] =
173 nlohmann::json::array({confData.baseDN});
174 ldap["LDAPService"]["SearchSettings"]["UsernameAttribute"] =
175 confData.userNameAttribute;
176 ldap["LDAPService"]["SearchSettings"]["GroupsAttribute"] =
177 confData.groupAttribute;
178
179 nlohmann::json& roleMapArray = ldap["RemoteRoleMapping"];
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600180 roleMapArray = nlohmann::json::array();
Ed Tanous9eb808c2022-01-25 10:19:23 -0800181 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600182 {
183 BMCWEB_LOG_DEBUG << "Pushing the data groupName="
184 << obj.second.groupName << "\n";
185 roleMapArray.push_back(
186 {nlohmann::json::array({"RemoteGroup", obj.second.groupName}),
187 nlohmann::json::array(
188 {"LocalRole", getRoleIdFromPrivilege(obj.second.privilege)})});
189 }
Ratan Gupta6973a582018-12-13 18:25:44 +0530190}
191
192/**
Ratan Gupta06785242019-07-26 22:30:16 +0530193 * @brief validates given JSON input and then calls appropriate method to
194 * create, to delete or to set Rolemapping object based on the given input.
195 *
196 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000197inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800198 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530199 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousf23b7292020-10-15 09:41:17 -0700200 const std::string& serverType, const std::vector<nlohmann::json>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530201{
202 for (size_t index = 0; index < input.size(); index++)
203 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700204 const nlohmann::json& thisJson = input[index];
Ratan Gupta06785242019-07-26 22:30:16 +0530205
206 if (thisJson.is_null())
207 {
208 // delete the existing object
209 if (index < roleMapObjData.size())
210 {
211 crow::connections::systemBus->async_method_call(
212 [asyncResp, roleMapObjData, serverType,
213 index](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700214 if (ec)
215 {
216 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
217 messages::internalError(asyncResp->res);
218 return;
219 }
220 asyncResp->res
221 .jsonValue[serverType]["RemoteRoleMapping"][index] =
222 nullptr;
Ratan Gupta06785242019-07-26 22:30:16 +0530223 },
224 ldapDbusService, roleMapObjData[index].first,
225 "xyz.openbmc_project.Object.Delete", "Delete");
226 }
227 else
228 {
229 BMCWEB_LOG_ERROR << "Can't delete the object";
230 messages::propertyValueTypeError(
Ed Tanous71f52d92021-02-19 08:51:17 -0800231 asyncResp->res,
232 thisJson.dump(2, ' ', true,
233 nlohmann::json::error_handler_t::replace),
Ratan Gupta06785242019-07-26 22:30:16 +0530234 "RemoteRoleMapping/" + std::to_string(index));
235 return;
236 }
237 }
238 else if (thisJson.empty())
239 {
240 // Don't do anything for the empty objects,parse next json
241 // eg {"RemoteRoleMapping",[{}]}
242 }
243 else
244 {
245 // update/create the object
246 std::optional<std::string> remoteGroup;
247 std::optional<std::string> localRole;
248
Ed Tanousf23b7292020-10-15 09:41:17 -0700249 // This is a copy, but it's required in this case because of how
250 // readJson is structured
251 nlohmann::json thisJsonCopy = thisJson;
252 if (!json_util::readJson(thisJsonCopy, asyncResp->res,
253 "RemoteGroup", remoteGroup, "LocalRole",
254 localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530255 {
256 continue;
257 }
258
259 // Update existing RoleMapping Object
260 if (index < roleMapObjData.size())
261 {
262 BMCWEB_LOG_DEBUG << "Update Role Map Object";
263 // If "RemoteGroup" info is provided
264 if (remoteGroup)
265 {
266 crow::connections::systemBus->async_method_call(
267 [asyncResp, roleMapObjData, serverType, index,
268 remoteGroup](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700269 if (ec)
270 {
271 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
272 messages::internalError(asyncResp->res);
273 return;
274 }
275 asyncResp->res
276 .jsonValue[serverType]["RemoteRoleMapping"][index]
277 ["RemoteGroup"] = *remoteGroup;
Ratan Gupta06785242019-07-26 22:30:16 +0530278 },
279 ldapDbusService, roleMapObjData[index].first,
280 propertyInterface, "Set",
281 "xyz.openbmc_project.User.PrivilegeMapperEntry",
282 "GroupName",
Ed Tanous168e20c2021-12-13 14:39:53 -0800283 dbus::utility::DbusVariantType(
284 std::move(*remoteGroup)));
Ratan Gupta06785242019-07-26 22:30:16 +0530285 }
286
287 // If "LocalRole" info is provided
288 if (localRole)
289 {
290 crow::connections::systemBus->async_method_call(
291 [asyncResp, roleMapObjData, serverType, index,
292 localRole](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700293 if (ec)
294 {
295 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
296 messages::internalError(asyncResp->res);
297 return;
298 }
299 asyncResp->res
300 .jsonValue[serverType]["RemoteRoleMapping"][index]
301 ["LocalRole"] = *localRole;
Ratan Gupta06785242019-07-26 22:30:16 +0530302 },
303 ldapDbusService, roleMapObjData[index].first,
304 propertyInterface, "Set",
305 "xyz.openbmc_project.User.PrivilegeMapperEntry",
306 "Privilege",
Ed Tanous168e20c2021-12-13 14:39:53 -0800307 dbus::utility::DbusVariantType(
Ratan Gupta06785242019-07-26 22:30:16 +0530308 getPrivilegeFromRoleId(std::move(*localRole))));
309 }
310 }
311 // Create a new RoleMapping Object.
312 else
313 {
314 BMCWEB_LOG_DEBUG
315 << "setRoleMappingProperties: Creating new Object";
316 std::string pathString =
317 "RemoteRoleMapping/" + std::to_string(index);
318
319 if (!localRole)
320 {
321 messages::propertyMissing(asyncResp->res,
322 pathString + "/LocalRole");
323 continue;
324 }
325 if (!remoteGroup)
326 {
327 messages::propertyMissing(asyncResp->res,
328 pathString + "/RemoteGroup");
329 continue;
330 }
331
332 std::string dbusObjectPath;
333 if (serverType == "ActiveDirectory")
334 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700335 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530336 }
337 else if (serverType == "LDAP")
338 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000339 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530340 }
341
342 BMCWEB_LOG_DEBUG << "Remote Group=" << *remoteGroup
343 << ",LocalRole=" << *localRole;
344
345 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700346 [asyncResp, serverType, localRole,
Ratan Gupta06785242019-07-26 22:30:16 +0530347 remoteGroup](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700348 if (ec)
349 {
350 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
351 messages::internalError(asyncResp->res);
352 return;
353 }
354 nlohmann::json& remoteRoleJson =
355 asyncResp->res
356 .jsonValue[serverType]["RemoteRoleMapping"];
357 nlohmann::json::object_t roleMapEntry;
358 roleMapEntry["LocalRole"] = *localRole;
359 roleMapEntry["RemoteGroup"] = *remoteGroup;
360 remoteRoleJson.push_back(std::move(roleMapEntry));
Ratan Gupta06785242019-07-26 22:30:16 +0530361 },
362 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700363 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530364 getPrivilegeFromRoleId(std::move(*localRole)));
365 }
366 }
367 }
368}
369
370/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530371 * Function that retrieves all properties for LDAP config object
372 * into JSON
373 */
374template <typename CallbackFunc>
375inline void getLDAPConfigData(const std::string& ldapType,
376 CallbackFunc&& callback)
377{
Ratan Guptaab828d72019-04-22 14:18:33 +0530378
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600379 const std::array<const char*, 2> interfaces = {ldapEnableInterface,
Ratan Gupta6973a582018-12-13 18:25:44 +0530380 ldapConfigInterface};
381
382 crow::connections::systemBus->async_method_call(
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600383 [callback, ldapType](const boost::system::error_code ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800384 const dbus::utility::MapperGetObject& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700385 if (ec || resp.empty())
386 {
387 BMCWEB_LOG_ERROR
388 << "DBUS response error during getting of service name: " << ec;
389 LDAPConfigData empty{};
390 callback(false, empty, ldapType);
391 return;
392 }
393 std::string service = resp.begin()->first;
394 crow::connections::systemBus->async_method_call(
395 [callback,
396 ldapType](const boost::system::error_code errorCode,
397 const dbus::utility::ManagedObjectType& ldapObjects) {
398 LDAPConfigData confData{};
399 if (errorCode)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600400 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700401 callback(false, confData, ldapType);
402 BMCWEB_LOG_ERROR << "D-Bus responses error: " << errorCode;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600403 return;
404 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600405
Ed Tanous002d39b2022-05-31 08:59:27 -0700406 std::string ldapDbusType;
407 std::string searchString;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600408
Ed Tanous002d39b2022-05-31 08:59:27 -0700409 if (ldapType == "LDAP")
410 {
411 ldapDbusType =
412 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
413 searchString = "openldap";
414 }
415 else if (ldapType == "ActiveDirectory")
416 {
417 ldapDbusType =
418 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
419 searchString = "active_directory";
420 }
421 else
422 {
423 BMCWEB_LOG_ERROR << "Can't get the DbusType for the given type="
424 << ldapType;
425 callback(false, confData, ldapType);
426 return;
427 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600428
Ed Tanous002d39b2022-05-31 08:59:27 -0700429 std::string ldapEnableInterfaceStr = ldapEnableInterface;
430 std::string ldapConfigInterfaceStr = ldapConfigInterface;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600431
Ed Tanous002d39b2022-05-31 08:59:27 -0700432 for (const auto& object : ldapObjects)
433 {
434 // let's find the object whose ldap type is equal to the
435 // given type
436 if (object.first.str.find(searchString) == std::string::npos)
437 {
438 continue;
439 }
440
441 for (const auto& interface : object.second)
442 {
443 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600444 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700445 // rest of the properties are string.
446 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600447 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700448 if (property.first == "Enabled")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600449 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700450 const bool* value =
451 std::get_if<bool>(&property.second);
452 if (value == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600453 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700454 continue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600455 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700456 confData.serviceEnabled = *value;
457 break;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600458 }
459 }
460 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700461 else if (interface.first == ldapConfigInterfaceStr)
462 {
463
464 for (const auto& property : interface.second)
465 {
466 const std::string* strValue =
467 std::get_if<std::string>(&property.second);
468 if (strValue == nullptr)
469 {
470 continue;
471 }
472 if (property.first == "LDAPServerURI")
473 {
474 confData.uri = *strValue;
475 }
476 else if (property.first == "LDAPBindDN")
477 {
478 confData.bindDN = *strValue;
479 }
480 else if (property.first == "LDAPBaseDN")
481 {
482 confData.baseDN = *strValue;
483 }
484 else if (property.first == "LDAPSearchScope")
485 {
486 confData.searchScope = *strValue;
487 }
488 else if (property.first == "GroupNameAttribute")
489 {
490 confData.groupAttribute = *strValue;
491 }
492 else if (property.first == "UserNameAttribute")
493 {
494 confData.userNameAttribute = *strValue;
495 }
496 else if (property.first == "LDAPType")
497 {
498 confData.serverType = *strValue;
499 }
500 }
501 }
502 else if (interface.first ==
503 "xyz.openbmc_project.User.PrivilegeMapperEntry")
504 {
505 LDAPRoleMapData roleMapData{};
506 for (const auto& property : interface.second)
507 {
508 const std::string* strValue =
509 std::get_if<std::string>(&property.second);
510
511 if (strValue == nullptr)
512 {
513 continue;
514 }
515
516 if (property.first == "GroupName")
517 {
518 roleMapData.groupName = *strValue;
519 }
520 else if (property.first == "Privilege")
521 {
522 roleMapData.privilege = *strValue;
523 }
524 }
525
526 confData.groupRoleList.emplace_back(object.first.str,
527 roleMapData);
528 }
529 }
530 }
531 callback(true, confData, ldapType);
532 },
533 service, ldapRootObject, dbusObjManagerIntf, "GetManagedObjects");
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600534 },
535 mapperBusName, mapperObjectPath, mapperIntf, "GetObject",
Ed Tanous23a21a12020-07-25 04:45:05 +0000536 ldapConfigObjectName, interfaces);
Ratan Gupta6973a582018-12-13 18:25:44 +0530537}
538
Ed Tanous6c51eab2021-06-03 12:30:29 -0700539/**
540 * @brief parses the authentication section under the LDAP
541 * @param input JSON data
542 * @param asyncResp pointer to the JSON response
543 * @param userName userName to be filled from the given JSON.
544 * @param password password to be filled from the given JSON.
545 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700546inline void parseLDAPAuthenticationJson(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700547 nlohmann::json input, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
548 std::optional<std::string>& username, std::optional<std::string>& password)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549{
Ed Tanous6c51eab2021-06-03 12:30:29 -0700550 std::optional<std::string> authType;
551
552 if (!json_util::readJson(input, asyncResp->res, "AuthenticationType",
553 authType, "Username", username, "Password",
554 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700555 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700556 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700557 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700558 if (!authType)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530559 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700560 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530561 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700562 if (*authType != "UsernameAndPassword")
Ratan Gupta8a07d282019-03-16 08:33:47 +0530563 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700564 messages::propertyValueNotInList(asyncResp->res, *authType,
565 "AuthenticationType");
566 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530567 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700568}
569/**
570 * @brief parses the LDAPService section under the LDAP
571 * @param input JSON data
572 * @param asyncResp pointer to the JSON response
573 * @param baseDNList baseDN to be filled from the given JSON.
574 * @param userNameAttribute userName to be filled from the given JSON.
575 * @param groupaAttribute password to be filled from the given JSON.
576 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530577
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700578inline void
579 parseLDAPServiceJson(nlohmann::json input,
580 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
581 std::optional<std::vector<std::string>>& baseDNList,
582 std::optional<std::string>& userNameAttribute,
583 std::optional<std::string>& groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700584{
585 std::optional<nlohmann::json> searchSettings;
586
587 if (!json_util::readJson(input, asyncResp->res, "SearchSettings",
588 searchSettings))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530589 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700590 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530591 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700592 if (!searchSettings)
Ratan Gupta8a07d282019-03-16 08:33:47 +0530593 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700594 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530595 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700596 if (!json_util::readJson(*searchSettings, asyncResp->res,
597 "BaseDistinguishedNames", baseDNList,
598 "UsernameAttribute", userNameAttribute,
599 "GroupsAttribute", groupsAttribute))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530600 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700601 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +0530602 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700603}
604/**
605 * @brief updates the LDAP server address and updates the
606 json response with the new value.
607 * @param serviceAddressList address to be updated.
608 * @param asyncResp pointer to the JSON response
609 * @param ldapServerElementName Type of LDAP
610 server(openLDAP/ActiveDirectory)
611 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530612
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700613inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700614 const std::vector<std::string>& serviceAddressList,
615 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
616 const std::string& ldapServerElementName,
617 const std::string& ldapConfigObject)
618{
619 crow::connections::systemBus->async_method_call(
620 [asyncResp, ldapServerElementName,
621 serviceAddressList](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700622 if (ec)
623 {
624 BMCWEB_LOG_DEBUG
625 << "Error Occurred in updating the service address";
626 messages::internalError(asyncResp->res);
627 return;
628 }
629 std::vector<std::string> modifiedserviceAddressList = {
630 serviceAddressList.front()};
631 asyncResp->res.jsonValue[ldapServerElementName]["ServiceAddresses"] =
632 modifiedserviceAddressList;
633 if ((serviceAddressList).size() > 1)
634 {
635 messages::propertyValueModified(asyncResp->res, "ServiceAddresses",
636 serviceAddressList.front());
637 }
638 BMCWEB_LOG_DEBUG << "Updated the service address";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700639 },
640 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
641 ldapConfigInterface, "LDAPServerURI",
Ed Tanous168e20c2021-12-13 14:39:53 -0800642 dbus::utility::DbusVariantType(serviceAddressList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700643}
644/**
645 * @brief updates the LDAP Bind DN and updates the
646 json response with the new value.
647 * @param username name of the user which needs to be updated.
648 * @param asyncResp pointer to the JSON response
649 * @param ldapServerElementName Type of LDAP
650 server(openLDAP/ActiveDirectory)
651 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530652
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700653inline void
654 handleUserNamePatch(const std::string& username,
655 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
656 const std::string& ldapServerElementName,
657 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700658{
659 crow::connections::systemBus->async_method_call(
660 [asyncResp, username,
661 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700662 if (ec)
663 {
664 BMCWEB_LOG_DEBUG << "Error occurred in updating the username";
665 messages::internalError(asyncResp->res);
666 return;
667 }
668 asyncResp->res
669 .jsonValue[ldapServerElementName]["Authentication"]["Username"] =
670 username;
671 BMCWEB_LOG_DEBUG << "Updated the username";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700672 },
673 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800674 ldapConfigInterface, "LDAPBindDN",
675 dbus::utility::DbusVariantType(username));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700676}
677
678/**
679 * @brief updates the LDAP password
680 * @param password : ldap password which needs to be updated.
681 * @param asyncResp pointer to the JSON response
682 * @param ldapServerElementName Type of LDAP
683 * server(openLDAP/ActiveDirectory)
684 */
685
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700686inline void
687 handlePasswordPatch(const std::string& password,
688 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
689 const std::string& ldapServerElementName,
690 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700691{
692 crow::connections::systemBus->async_method_call(
693 [asyncResp, password,
694 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700695 if (ec)
696 {
697 BMCWEB_LOG_DEBUG << "Error occurred in updating the password";
698 messages::internalError(asyncResp->res);
699 return;
700 }
701 asyncResp->res
702 .jsonValue[ldapServerElementName]["Authentication"]["Password"] =
703 "";
704 BMCWEB_LOG_DEBUG << "Updated the password";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700705 },
706 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
707 ldapConfigInterface, "LDAPBindDNPassword",
Ed Tanous168e20c2021-12-13 14:39:53 -0800708 dbus::utility::DbusVariantType(password));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700709}
710
711/**
712 * @brief updates the LDAP BaseDN and updates the
713 json response with the new value.
714 * @param baseDNList baseDN list which needs to be updated.
715 * @param asyncResp pointer to the JSON response
716 * @param ldapServerElementName Type of LDAP
717 server(openLDAP/ActiveDirectory)
718 */
719
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700720inline void
721 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
722 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
723 const std::string& ldapServerElementName,
724 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700725{
726 crow::connections::systemBus->async_method_call(
727 [asyncResp, baseDNList,
728 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700729 if (ec)
730 {
731 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the base DN";
732 messages::internalError(asyncResp->res);
733 return;
734 }
735 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
736 auto& searchSettingsJson =
737 serverTypeJson["LDAPService"]["SearchSettings"];
738 std::vector<std::string> modifiedBaseDNList = {baseDNList.front()};
739 searchSettingsJson["BaseDistinguishedNames"] = modifiedBaseDNList;
740 if (baseDNList.size() > 1)
741 {
742 messages::propertyValueModified(
743 asyncResp->res, "BaseDistinguishedNames", baseDNList.front());
744 }
745 BMCWEB_LOG_DEBUG << "Updated the base DN";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700746 },
747 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
748 ldapConfigInterface, "LDAPBaseDN",
Ed Tanous168e20c2021-12-13 14:39:53 -0800749 dbus::utility::DbusVariantType(baseDNList.front()));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700750}
751/**
752 * @brief updates the LDAP user name attribute and updates the
753 json response with the new value.
754 * @param userNameAttribute attribute to be updated.
755 * @param asyncResp pointer to the JSON response
756 * @param ldapServerElementName Type of LDAP
757 server(openLDAP/ActiveDirectory)
758 */
759
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700760inline void
761 handleUserNameAttrPatch(const std::string& userNameAttribute,
762 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
763 const std::string& ldapServerElementName,
764 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700765{
766 crow::connections::systemBus->async_method_call(
767 [asyncResp, userNameAttribute,
768 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700769 if (ec)
770 {
771 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
772 "username attribute";
773 messages::internalError(asyncResp->res);
774 return;
775 }
776 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
777 auto& searchSettingsJson =
778 serverTypeJson["LDAPService"]["SearchSettings"];
779 searchSettingsJson["UsernameAttribute"] = userNameAttribute;
780 BMCWEB_LOG_DEBUG << "Updated the user name attr.";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700781 },
782 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
783 ldapConfigInterface, "UserNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800784 dbus::utility::DbusVariantType(userNameAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700785}
786/**
787 * @brief updates the LDAP group attribute and updates the
788 json response with the new value.
789 * @param groupsAttribute attribute to be updated.
790 * @param asyncResp pointer to the JSON response
791 * @param ldapServerElementName Type of LDAP
792 server(openLDAP/ActiveDirectory)
793 */
794
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700795inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700796 const std::string& groupsAttribute,
797 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
798 const std::string& ldapServerElementName,
799 const std::string& ldapConfigObject)
800{
801 crow::connections::systemBus->async_method_call(
802 [asyncResp, groupsAttribute,
803 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700804 if (ec)
805 {
806 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the "
807 "groupname attribute";
808 messages::internalError(asyncResp->res);
809 return;
810 }
811 auto& serverTypeJson = asyncResp->res.jsonValue[ldapServerElementName];
812 auto& searchSettingsJson =
813 serverTypeJson["LDAPService"]["SearchSettings"];
814 searchSettingsJson["GroupsAttribute"] = groupsAttribute;
815 BMCWEB_LOG_DEBUG << "Updated the groupname attr";
Ed Tanous6c51eab2021-06-03 12:30:29 -0700816 },
817 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
818 ldapConfigInterface, "GroupNameAttribute",
Ed Tanous168e20c2021-12-13 14:39:53 -0800819 dbus::utility::DbusVariantType(groupsAttribute));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700820}
821/**
822 * @brief updates the LDAP service enable and updates the
823 json response with the new value.
824 * @param input JSON data.
825 * @param asyncResp pointer to the JSON response
826 * @param ldapServerElementName Type of LDAP
827 server(openLDAP/ActiveDirectory)
828 */
829
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700830inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700831 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
832 const std::string& ldapServerElementName,
833 const std::string& ldapConfigObject)
834{
835 crow::connections::systemBus->async_method_call(
836 [asyncResp, serviceEnabled,
837 ldapServerElementName](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700838 if (ec)
839 {
840 BMCWEB_LOG_DEBUG << "Error Occurred in Updating the service enable";
841 messages::internalError(asyncResp->res);
842 return;
843 }
844 asyncResp->res.jsonValue[ldapServerElementName]["ServiceEnabled"] =
845 serviceEnabled;
846 BMCWEB_LOG_DEBUG << "Updated Service enable = " << serviceEnabled;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700847 },
848 ldapDbusService, ldapConfigObject, propertyInterface, "Set",
Ed Tanous168e20c2021-12-13 14:39:53 -0800849 ldapEnableInterface, "Enabled",
850 dbus::utility::DbusVariantType(serviceEnabled));
Ed Tanous6c51eab2021-06-03 12:30:29 -0700851}
852
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700853inline void
854 handleAuthMethodsPatch(nlohmann::json& input,
855 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700856{
857 std::optional<bool> basicAuth;
858 std::optional<bool> cookie;
859 std::optional<bool> sessionToken;
860 std::optional<bool> xToken;
861 std::optional<bool> tls;
862
863 if (!json_util::readJson(input, asyncResp->res, "BasicAuth", basicAuth,
864 "Cookie", cookie, "SessionToken", sessionToken,
865 "XToken", xToken, "TLS", tls))
Ratan Gupta8a07d282019-03-16 08:33:47 +0530866 {
Ed Tanous6c51eab2021-06-03 12:30:29 -0700867 BMCWEB_LOG_ERROR << "Cannot read values from AuthMethod tag";
868 return;
869 }
870
871 // Make a copy of methods configuration
872 persistent_data::AuthConfigMethods authMethodsConfig =
873 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
874
875 if (basicAuth)
876 {
877#ifndef BMCWEB_ENABLE_BASIC_AUTHENTICATION
878 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800879 asyncResp->res,
880 "Setting BasicAuth when basic-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700881 return;
882#endif
883 authMethodsConfig.basic = *basicAuth;
884 }
885
886 if (cookie)
887 {
888#ifndef BMCWEB_ENABLE_COOKIE_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800889 messages::actionNotSupported(
890 asyncResp->res,
891 "Setting Cookie when cookie-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700892 return;
893#endif
894 authMethodsConfig.cookie = *cookie;
895 }
896
897 if (sessionToken)
898 {
899#ifndef BMCWEB_ENABLE_SESSION_AUTHENTICATION
900 messages::actionNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800901 asyncResp->res,
902 "Setting SessionToken when session-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700903 return;
904#endif
905 authMethodsConfig.sessionToken = *sessionToken;
906 }
907
908 if (xToken)
909 {
910#ifndef BMCWEB_ENABLE_XTOKEN_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800911 messages::actionNotSupported(
912 asyncResp->res,
913 "Setting XToken when xtoken-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700914 return;
915#endif
916 authMethodsConfig.xtoken = *xToken;
917 }
918
919 if (tls)
920 {
921#ifndef BMCWEB_ENABLE_MUTUAL_TLS_AUTHENTICATION
George Liu0fda0f12021-11-16 10:06:17 +0800922 messages::actionNotSupported(
923 asyncResp->res,
924 "Setting TLS when mutual-tls-auth feature is disabled");
Ed Tanous6c51eab2021-06-03 12:30:29 -0700925 return;
926#endif
927 authMethodsConfig.tls = *tls;
928 }
929
930 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
931 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
932 !authMethodsConfig.tls)
933 {
934 // Do not allow user to disable everything
935 messages::actionNotSupported(asyncResp->res,
936 "of disabling all available methods");
937 return;
938 }
939
940 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
941 authMethodsConfig);
942 // Save configuration immediately
943 persistent_data::getConfig().writeData();
944
945 messages::success(asyncResp->res);
946}
947
948/**
949 * @brief Get the required values from the given JSON, validates the
950 * value and create the LDAP config object.
951 * @param input JSON data
952 * @param asyncResp pointer to the JSON response
953 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
954 */
955
956inline void handleLDAPPatch(nlohmann::json& input,
957 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
958 const std::string& serverType)
959{
960 std::string dbusObjectPath;
961 if (serverType == "ActiveDirectory")
962 {
963 dbusObjectPath = adConfigObject;
964 }
965 else if (serverType == "LDAP")
966 {
967 dbusObjectPath = ldapConfigObjectName;
968 }
969 else
970 {
971 return;
972 }
973
974 std::optional<nlohmann::json> authentication;
975 std::optional<nlohmann::json> ldapService;
976 std::optional<std::vector<std::string>> serviceAddressList;
977 std::optional<bool> serviceEnabled;
978 std::optional<std::vector<std::string>> baseDNList;
979 std::optional<std::string> userNameAttribute;
980 std::optional<std::string> groupsAttribute;
981 std::optional<std::string> userName;
982 std::optional<std::string> password;
983 std::optional<std::vector<nlohmann::json>> remoteRoleMapData;
984
985 if (!json_util::readJson(input, asyncResp->res, "Authentication",
986 authentication, "LDAPService", ldapService,
987 "ServiceAddresses", serviceAddressList,
988 "ServiceEnabled", serviceEnabled,
989 "RemoteRoleMapping", remoteRoleMapData))
990 {
991 return;
992 }
993
994 if (authentication)
995 {
996 parseLDAPAuthenticationJson(*authentication, asyncResp, userName,
997 password);
998 }
999 if (ldapService)
1000 {
1001 parseLDAPServiceJson(*ldapService, asyncResp, baseDNList,
1002 userNameAttribute, groupsAttribute);
1003 }
1004 if (serviceAddressList)
1005 {
Ed Tanous26f69762022-01-25 09:49:11 -08001006 if (serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +05301007 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001008 messages::propertyValueNotInList(asyncResp->res, "[]",
1009 "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +00001010 return;
1011 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001012 }
1013 if (baseDNList)
1014 {
Ed Tanous26f69762022-01-25 09:49:11 -08001015 if (baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +05301016 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001017 messages::propertyValueNotInList(asyncResp->res, "[]",
1018 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301019 return;
1020 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001021 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301022
Ed Tanous6c51eab2021-06-03 12:30:29 -07001023 // nothing to update, then return
1024 if (!userName && !password && !serviceAddressList && !baseDNList &&
1025 !userNameAttribute && !groupsAttribute && !serviceEnabled &&
1026 !remoteRoleMapData)
1027 {
1028 return;
1029 }
1030
1031 // Get the existing resource first then keep modifying
1032 // whenever any property gets updated.
Ed Tanous002d39b2022-05-31 08:59:27 -07001033 getLDAPConfigData(
1034 serverType,
1035 [asyncResp, userName, password, baseDNList, userNameAttribute,
1036 groupsAttribute, serviceAddressList, serviceEnabled, dbusObjectPath,
1037 remoteRoleMapData](bool success, const LDAPConfigData& confData,
1038 const std::string& serverT) {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001039 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301040 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001041 messages::internalError(asyncResp->res);
1042 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301043 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001044 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1045 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301046 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001047 // Disable the service first and update the rest of
1048 // the properties.
1049 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301050 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001051
Ratan Gupta8a07d282019-03-16 08:33:47 +05301052 if (serviceAddressList)
1053 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001054 handleServiceAddressPatch(*serviceAddressList, asyncResp, serverT,
1055 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301056 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001057 if (userName)
1058 {
1059 handleUserNamePatch(*userName, asyncResp, serverT, dbusObjectPath);
1060 }
1061 if (password)
1062 {
1063 handlePasswordPatch(*password, asyncResp, serverT, dbusObjectPath);
1064 }
1065
Ratan Gupta8a07d282019-03-16 08:33:47 +05301066 if (baseDNList)
1067 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001068 handleBaseDNPatch(*baseDNList, asyncResp, serverT, dbusObjectPath);
1069 }
1070 if (userNameAttribute)
1071 {
1072 handleUserNameAttrPatch(*userNameAttribute, asyncResp, serverT,
1073 dbusObjectPath);
1074 }
1075 if (groupsAttribute)
1076 {
1077 handleGroupNameAttrPatch(*groupsAttribute, asyncResp, serverT,
1078 dbusObjectPath);
1079 }
1080 if (serviceEnabled)
1081 {
1082 // if user has given the value as true then enable
1083 // the service. if user has given false then no-op
1084 // as service is already stopped.
1085 if (*serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301086 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001087 handleServiceEnablePatch(*serviceEnabled, asyncResp, serverT,
1088 dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301089 }
1090 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001091 else
1092 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001093 // if user has not given the service enabled value
1094 // then revert it to the same state as it was
1095 // before.
1096 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1097 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001098 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001099
Ed Tanous6c51eab2021-06-03 12:30:29 -07001100 if (remoteRoleMapData)
1101 {
1102 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
1103 *remoteRoleMapData);
1104 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001105 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001106}
1107
1108inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
1109 const std::string& username,
1110 std::optional<std::string> password,
1111 std::optional<bool> enabled,
1112 std::optional<std::string> roleId,
1113 std::optional<bool> locked)
1114{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301115 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1116 tempObjPath /= username;
1117 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001118
1119 dbus::utility::checkDbusPathExists(
1120 dbusObjectPath,
Ed Tanous11063332021-09-24 11:55:44 -07001121 [dbusObjectPath, username, password(std::move(password)),
1122 roleId(std::move(roleId)), enabled, locked,
1123 asyncResp{std::move(asyncResp)}](int rc) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001124 if (rc <= 0)
1125 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001126 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
Ed Tanous002d39b2022-05-31 08:59:27 -07001127 username);
1128 return;
1129 }
1130
1131 if (password)
1132 {
1133 int retval = pamUpdatePassword(username, *password);
1134
1135 if (retval == PAM_USER_UNKNOWN)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001136 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001137 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1138 username);
Ed Tanous002d39b2022-05-31 08:59:27 -07001139 }
1140 else if (retval == PAM_AUTHTOK_ERR)
1141 {
1142 // If password is invalid
1143 messages::propertyValueFormatError(asyncResp->res, *password,
1144 "Password");
1145 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1146 }
1147 else if (retval != PAM_SUCCESS)
1148 {
1149 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001150 return;
1151 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001152 else
Ed Tanous6c51eab2021-06-03 12:30:29 -07001153 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001154 messages::success(asyncResp->res);
1155 }
1156 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001157
Ed Tanous002d39b2022-05-31 08:59:27 -07001158 if (enabled)
1159 {
1160 crow::connections::systemBus->async_method_call(
1161 [asyncResp](const boost::system::error_code ec) {
1162 if (ec)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001163 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001164 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
Ayushi Smriti599c71d2019-08-23 17:43:18 +00001165 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001166 return;
1167 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001168 messages::success(asyncResp->res);
1169 return;
1170 },
1171 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1172 "org.freedesktop.DBus.Properties", "Set",
1173 "xyz.openbmc_project.User.Attributes", "UserEnabled",
1174 dbus::utility::DbusVariantType{*enabled});
1175 }
1176
1177 if (roleId)
1178 {
1179 std::string priv = getPrivilegeFromRoleId(*roleId);
1180 if (priv.empty())
1181 {
1182 messages::propertyValueNotInList(asyncResp->res, *roleId,
1183 "RoleId");
1184 return;
1185 }
1186 if (priv == "priv-noaccess")
1187 {
1188 priv = "";
Ed Tanous6c51eab2021-06-03 12:30:29 -07001189 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001190
Ed Tanous002d39b2022-05-31 08:59:27 -07001191 crow::connections::systemBus->async_method_call(
1192 [asyncResp](const boost::system::error_code ec) {
1193 if (ec)
Ed Tanous04ae99e2018-09-20 15:54:36 -07001194 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001195 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1196 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001197 return;
1198 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001199 messages::success(asyncResp->res);
1200 },
1201 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1202 "org.freedesktop.DBus.Properties", "Set",
1203 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1204 dbus::utility::DbusVariantType{priv});
1205 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001206
Ed Tanous002d39b2022-05-31 08:59:27 -07001207 if (locked)
1208 {
1209 // admin can unlock the account which is locked by
1210 // successive authentication failures but admin should
1211 // not be allowed to lock an account.
1212 if (*locked)
1213 {
1214 messages::propertyValueNotInList(asyncResp->res, "true",
1215 "Locked");
1216 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -07001217 }
1218
Ed Tanous002d39b2022-05-31 08:59:27 -07001219 crow::connections::systemBus->async_method_call(
1220 [asyncResp](const boost::system::error_code ec) {
1221 if (ec)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001222 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001223 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1224 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -07001225 return;
1226 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001227 messages::success(asyncResp->res);
1228 return;
1229 },
1230 "xyz.openbmc_project.User.Manager", dbusObjectPath,
1231 "org.freedesktop.DBus.Properties", "Set",
1232 "xyz.openbmc_project.User.Attributes",
1233 "UserLockedForFailedAttempt",
1234 dbus::utility::DbusVariantType{*locked});
1235 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001236 });
1237}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001238
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001239inline void handleAccountServiceHead(
1240 App& app, const crow::Request& req,
1241 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001242{
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001243
Ed Tanous1ef4c342022-05-12 16:12:36 -07001244 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1245 {
1246 return;
1247 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001248 asyncResp->res.addHeader(
1249 boost::beast::http::field::link,
1250 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1251}
1252
1253inline void
1254 handleAccountServiceGet(App& app, const crow::Request& req,
1255 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1256{
1257 handleAccountServiceHead(app, req, asyncResp);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001258 const persistent_data::AuthConfigMethods& authMethodsConfig =
1259 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1260
1261 nlohmann::json& json = asyncResp->res.jsonValue;
1262 json["@odata.id"] = "/redfish/v1/AccountService";
1263 json["@odata.type"] = "#AccountService."
1264 "v1_10_0.AccountService";
1265 json["Id"] = "AccountService";
1266 json["Name"] = "Account Service";
1267 json["Description"] = "Account Service";
1268 json["ServiceEnabled"] = true;
1269 json["MaxPasswordLength"] = 20;
1270 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1271 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
1272 json["Oem"]["OpenBMC"]["@odata.type"] =
1273 "#OemAccountService.v1_0_0.AccountService";
1274 json["Oem"]["OpenBMC"]["@odata.id"] =
1275 "/redfish/v1/AccountService#/Oem/OpenBMC";
1276 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1277 authMethodsConfig.basic;
1278 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1279 authMethodsConfig.sessionToken;
1280 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1281 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1282 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1283
1284 // /redfish/v1/AccountService/LDAP/Certificates is something only
1285 // ConfigureManager can access then only display when the user has
1286 // permissions ConfigureManager
1287 Privileges effectiveUserPrivileges =
1288 redfish::getUserPrivileges(req.userRole);
1289
1290 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1291 effectiveUserPrivileges))
1292 {
1293 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1294 "/redfish/v1/AccountService/LDAP/Certificates";
1295 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001296 sdbusplus::asio::getAllProperties(
1297 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1298 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.AccountPolicy",
Ed Tanous1ef4c342022-05-12 16:12:36 -07001299 [asyncResp](const boost::system::error_code ec,
1300 const dbus::utility::DBusPropertiesMap& propertiesList) {
1301 if (ec)
1302 {
1303 messages::internalError(asyncResp->res);
1304 return;
1305 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001306
Ed Tanous1ef4c342022-05-12 16:12:36 -07001307 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size()
1308 << "properties for AccountService";
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001309
1310 const uint8_t* minPasswordLength = nullptr;
1311 const uint32_t* accountUnlockTimeout = nullptr;
1312 const uint16_t* maxLoginAttemptBeforeLockout = nullptr;
1313
1314 const bool success = sdbusplus::unpackPropertiesNoThrow(
1315 dbus_utils::UnpackErrorPrinter(), propertiesList,
1316 "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout",
1317 accountUnlockTimeout, "MaxLoginAttemptBeforeLockout",
1318 maxLoginAttemptBeforeLockout);
1319
1320 if (!success)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001321 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001322 messages::internalError(asyncResp->res);
1323 return;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001324 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001325
1326 if (minPasswordLength != nullptr)
1327 {
1328 asyncResp->res.jsonValue["MinPasswordLength"] = *minPasswordLength;
1329 }
1330
1331 if (accountUnlockTimeout != nullptr)
1332 {
1333 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1334 *accountUnlockTimeout;
1335 }
1336
1337 if (maxLoginAttemptBeforeLockout != nullptr)
1338 {
1339 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1340 *maxLoginAttemptBeforeLockout;
1341 }
1342 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001343
Ed Tanous02cad962022-06-30 16:50:15 -07001344 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001345 const std::string& ldapType) {
1346 if (!success)
1347 {
1348 return;
1349 }
1350 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1351 };
1352
1353 getLDAPConfigData("LDAP", callback);
1354 getLDAPConfigData("ActiveDirectory", callback);
1355}
1356
1357inline void handleAccountServicePatch(
1358 App& app, const crow::Request& req,
1359 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1360{
1361 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1362 {
1363 return;
1364 }
1365 std::optional<uint32_t> unlockTimeout;
1366 std::optional<uint16_t> lockoutThreshold;
1367 std::optional<uint8_t> minPasswordLength;
1368 std::optional<uint16_t> maxPasswordLength;
1369 std::optional<nlohmann::json> ldapObject;
1370 std::optional<nlohmann::json> activeDirectoryObject;
1371 std::optional<nlohmann::json> oemObject;
1372
1373 if (!json_util::readJsonPatch(
1374 req, asyncResp->res, "AccountLockoutDuration", unlockTimeout,
1375 "AccountLockoutThreshold", lockoutThreshold, "MaxPasswordLength",
1376 maxPasswordLength, "MinPasswordLength", minPasswordLength, "LDAP",
1377 ldapObject, "ActiveDirectory", activeDirectoryObject, "Oem",
1378 oemObject))
1379 {
1380 return;
1381 }
1382
1383 if (minPasswordLength)
1384 {
1385 crow::connections::systemBus->async_method_call(
1386 [asyncResp](const boost::system::error_code ec) {
1387 if (ec)
1388 {
1389 messages::internalError(asyncResp->res);
1390 return;
1391 }
1392 messages::success(asyncResp->res);
1393 },
1394 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1395 "org.freedesktop.DBus.Properties", "Set",
1396 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
1397 dbus::utility::DbusVariantType(*minPasswordLength));
1398 }
1399
1400 if (maxPasswordLength)
1401 {
1402 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1403 }
1404
1405 if (ldapObject)
1406 {
1407 handleLDAPPatch(*ldapObject, asyncResp, "LDAP");
1408 }
1409
1410 if (std::optional<nlohmann::json> oemOpenBMCObject;
1411 oemObject && json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
1412 oemOpenBMCObject))
1413 {
1414 if (std::optional<nlohmann::json> authMethodsObject;
1415 oemOpenBMCObject &&
1416 json_util::readJson(*oemOpenBMCObject, asyncResp->res,
1417 "AuthMethods", authMethodsObject))
1418 {
1419 if (authMethodsObject)
1420 {
1421 handleAuthMethodsPatch(*authMethodsObject, asyncResp);
1422 }
1423 }
1424 }
1425
1426 if (activeDirectoryObject)
1427 {
1428 handleLDAPPatch(*activeDirectoryObject, asyncResp, "ActiveDirectory");
1429 }
1430
1431 if (unlockTimeout)
1432 {
1433 crow::connections::systemBus->async_method_call(
1434 [asyncResp](const boost::system::error_code ec) {
1435 if (ec)
1436 {
1437 messages::internalError(asyncResp->res);
1438 return;
1439 }
1440 messages::success(asyncResp->res);
1441 },
1442 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1443 "org.freedesktop.DBus.Properties", "Set",
1444 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
1445 dbus::utility::DbusVariantType(*unlockTimeout));
1446 }
1447 if (lockoutThreshold)
1448 {
1449 crow::connections::systemBus->async_method_call(
1450 [asyncResp](const boost::system::error_code ec) {
1451 if (ec)
1452 {
1453 messages::internalError(asyncResp->res);
1454 return;
1455 }
1456 messages::success(asyncResp->res);
1457 },
1458 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1459 "org.freedesktop.DBus.Properties", "Set",
1460 "xyz.openbmc_project.User.AccountPolicy",
1461 "MaxLoginAttemptBeforeLockout",
1462 dbus::utility::DbusVariantType(*lockoutThreshold));
1463 }
1464}
1465
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001466inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001467 App& app, const crow::Request& req,
1468 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1469{
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001470
Ed Tanous1ef4c342022-05-12 16:12:36 -07001471 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1472 {
1473 return;
1474 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001475 asyncResp->res.addHeader(
1476 boost::beast::http::field::link,
1477 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1478}
1479
1480inline void handleAccountCollectionGet(
1481 App& app, const crow::Request& req,
1482 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1483{
1484 handleAccountCollectionHead(app, req, asyncResp);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001485
1486 asyncResp->res.jsonValue["@odata.id"] =
1487 "/redfish/v1/AccountService/Accounts";
1488 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1489 "ManagerAccountCollection";
1490 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1491 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1492
1493 Privileges effectiveUserPrivileges =
1494 redfish::getUserPrivileges(req.userRole);
1495
1496 std::string thisUser;
1497 if (req.session)
1498 {
1499 thisUser = req.session->username;
1500 }
1501 crow::connections::systemBus->async_method_call(
1502 [asyncResp, thisUser, effectiveUserPrivileges](
1503 const boost::system::error_code ec,
1504 const dbus::utility::ManagedObjectType& users) {
1505 if (ec)
1506 {
1507 messages::internalError(asyncResp->res);
1508 return;
1509 }
1510
1511 bool userCanSeeAllAccounts =
1512 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1513
1514 bool userCanSeeSelf =
1515 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1516
1517 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1518 memberArray = nlohmann::json::array();
1519
1520 for (const auto& userpath : users)
1521 {
1522 std::string user = userpath.first.filename();
1523 if (user.empty())
1524 {
1525 messages::internalError(asyncResp->res);
1526 BMCWEB_LOG_ERROR << "Invalid firmware ID";
1527
1528 return;
1529 }
1530
1531 // As clarified by Redfish here:
1532 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1533 // Users without ConfigureUsers, only see their own
1534 // account. Users with ConfigureUsers, see all
1535 // accounts.
1536 if (userCanSeeAllAccounts || (thisUser == user && userCanSeeSelf))
1537 {
1538 nlohmann::json::object_t member;
1539 member["@odata.id"] =
1540 "/redfish/v1/AccountService/Accounts/" + user;
1541 memberArray.push_back(std::move(member));
1542 }
1543 }
1544 asyncResp->res.jsonValue["Members@odata.count"] = memberArray.size();
1545 },
1546 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1547 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1548}
1549
1550inline void handleAccountCollectionPost(
1551 App& app, const crow::Request& req,
1552 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1553{
1554 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1555 {
1556 return;
1557 }
1558 std::string username;
1559 std::string password;
1560 std::optional<std::string> roleId("User");
1561 std::optional<bool> enabled = true;
1562 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", username,
1563 "Password", password, "RoleId", roleId,
1564 "Enabled", enabled))
1565 {
1566 return;
1567 }
1568
1569 std::string priv = getPrivilegeFromRoleId(*roleId);
1570 if (priv.empty())
1571 {
1572 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
1573 return;
1574 }
1575 // TODO: Following override will be reverted once support in
1576 // phosphor-user-manager is added. In order to avoid dependency
1577 // issues, this is added in bmcweb, which will removed, once
1578 // phosphor-user-manager supports priv-noaccess.
1579 if (priv == "priv-noaccess")
1580 {
1581 roleId = "";
1582 }
1583 else
1584 {
1585 roleId = priv;
1586 }
1587
1588 // Reading AllGroups property
1589 sdbusplus::asio::getProperty<std::vector<std::string>>(
1590 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1591 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager",
1592 "AllGroups",
1593 [asyncResp, username, password{std::move(password)}, roleId,
1594 enabled](const boost::system::error_code ec,
1595 const std::vector<std::string>& allGroupsList) {
1596 if (ec)
1597 {
1598 BMCWEB_LOG_DEBUG << "ERROR with async_method_call";
1599 messages::internalError(asyncResp->res);
1600 return;
1601 }
1602
1603 if (allGroupsList.empty())
1604 {
1605 messages::internalError(asyncResp->res);
1606 return;
1607 }
1608
1609 crow::connections::systemBus->async_method_call(
1610 [asyncResp, username, password](const boost::system::error_code ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -05001611 sdbusplus::message_t& m) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001612 if (ec2)
1613 {
1614 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1615 return;
1616 }
1617
1618 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1619 {
1620 // At this point we have a user that's been
1621 // created, but the password set
1622 // failed.Something is wrong, so delete the user
1623 // that we've already created
1624 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1625 tempObjPath /= username;
1626 const std::string userPath(tempObjPath);
1627
1628 crow::connections::systemBus->async_method_call(
1629 [asyncResp, password](const boost::system::error_code ec3) {
1630 if (ec3)
1631 {
1632 messages::internalError(asyncResp->res);
1633 return;
1634 }
1635
1636 // If password is invalid
1637 messages::propertyValueFormatError(asyncResp->res, password,
1638 "Password");
1639 },
1640 "xyz.openbmc_project.User.Manager", userPath,
1641 "xyz.openbmc_project.Object.Delete", "Delete");
1642
1643 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
1644 return;
1645 }
1646
1647 messages::created(asyncResp->res);
1648 asyncResp->res.addHeader(
1649 "Location", "/redfish/v1/AccountService/Accounts/" + username);
1650 },
1651 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1652 "xyz.openbmc_project.User.Manager", "CreateUser", username,
1653 allGroupsList, *roleId, *enabled);
1654 });
1655}
1656
1657inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001658 handleAccountHead(App& app, const crow::Request& req,
1659 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1660 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001661{
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001662
Ed Tanous1ef4c342022-05-12 16:12:36 -07001663 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1664 {
1665 return;
1666 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001667 asyncResp->res.addHeader(
1668 boost::beast::http::field::link,
1669 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1670}
1671inline void
1672 handleAccountGet(App& app, const crow::Request& req,
1673 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1674 const std::string& accountName)
1675{
1676 handleAccountHead(app, req, asyncResp, accountName);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001677#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1678 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001679 messages::resourceNotFound(asyncResp->res, "ManagerAccount", accountName);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001680 return;
1681
1682#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1683 if (req.session == nullptr)
1684 {
1685 messages::internalError(asyncResp->res);
1686 return;
1687 }
1688 if (req.session->username != accountName)
1689 {
1690 // At this point we've determined that the user is trying to
1691 // modify a user that isn't them. We need to verify that they
1692 // have permissions to modify other users, so re-run the auth
1693 // check with the same permissions, minus ConfigureSelf.
1694 Privileges effectiveUserPrivileges =
1695 redfish::getUserPrivileges(req.userRole);
1696 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
1697 "ConfigureManager"};
1698 if (!effectiveUserPrivileges.isSupersetOf(
1699 requiredPermissionsToChangeNonSelf))
1700 {
1701 BMCWEB_LOG_DEBUG << "GET Account denied access";
1702 messages::insufficientPrivilege(asyncResp->res);
1703 return;
1704 }
1705 }
1706
1707 crow::connections::systemBus->async_method_call(
1708 [asyncResp,
1709 accountName](const boost::system::error_code ec,
1710 const dbus::utility::ManagedObjectType& users) {
1711 if (ec)
1712 {
1713 messages::internalError(asyncResp->res);
1714 return;
1715 }
1716 const auto userIt = std::find_if(
1717 users.begin(), users.end(),
1718 [accountName](
1719 const std::pair<sdbusplus::message::object_path,
1720 dbus::utility::DBusInteracesMap>& user) {
1721 return accountName == user.first.filename();
1722 });
1723
1724 if (userIt == users.end())
1725 {
1726 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1727 accountName);
1728 return;
1729 }
1730
1731 asyncResp->res.jsonValue["@odata.type"] =
1732 "#ManagerAccount.v1_4_0.ManagerAccount";
1733 asyncResp->res.jsonValue["Name"] = "User Account";
1734 asyncResp->res.jsonValue["Description"] = "User Account";
1735 asyncResp->res.jsonValue["Password"] = nullptr;
1736 asyncResp->res.jsonValue["AccountTypes"] = {"Redfish"};
1737
1738 for (const auto& interface : userIt->second)
1739 {
1740 if (interface.first == "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 << "UserEnabled wasn't a bool";
1751 messages::internalError(asyncResp->res);
1752 return;
1753 }
1754 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
1755 }
1756 else if (property.first == "UserLockedForFailedAttempt")
1757 {
1758 const bool* userLocked =
1759 std::get_if<bool>(&property.second);
1760 if (userLocked == nullptr)
1761 {
1762 BMCWEB_LOG_ERROR << "UserLockedForF"
1763 "ailedAttempt "
1764 "wasn't a bool";
1765 messages::internalError(asyncResp->res);
1766 return;
1767 }
1768 asyncResp->res.jsonValue["Locked"] = *userLocked;
1769 asyncResp->res
1770 .jsonValue["Locked@Redfish.AllowableValues"] = {
1771 "false"}; // can only unlock accounts
1772 }
1773 else if (property.first == "UserPrivilege")
1774 {
1775 const std::string* userPrivPtr =
1776 std::get_if<std::string>(&property.second);
1777 if (userPrivPtr == nullptr)
1778 {
1779 BMCWEB_LOG_ERROR << "UserPrivilege wasn't a "
1780 "string";
1781 messages::internalError(asyncResp->res);
1782 return;
1783 }
1784 std::string role = getRoleIdFromPrivilege(*userPrivPtr);
1785 if (role.empty())
1786 {
1787 BMCWEB_LOG_ERROR << "Invalid user role";
1788 messages::internalError(asyncResp->res);
1789 return;
1790 }
1791 asyncResp->res.jsonValue["RoleId"] = role;
1792
1793 nlohmann::json& roleEntry =
1794 asyncResp->res.jsonValue["Links"]["Role"];
1795 roleEntry["@odata.id"] =
1796 "/redfish/v1/AccountService/Roles/" + role;
1797 }
1798 else if (property.first == "UserPasswordExpired")
1799 {
1800 const bool* userPasswordExpired =
1801 std::get_if<bool>(&property.second);
1802 if (userPasswordExpired == nullptr)
1803 {
1804 BMCWEB_LOG_ERROR
1805 << "UserPasswordExpired wasn't a bool";
1806 messages::internalError(asyncResp->res);
1807 return;
1808 }
1809 asyncResp->res.jsonValue["PasswordChangeRequired"] =
1810 *userPasswordExpired;
1811 }
1812 }
1813 }
1814 }
1815
1816 asyncResp->res.jsonValue["@odata.id"] =
1817 "/redfish/v1/AccountService/Accounts/" + accountName;
1818 asyncResp->res.jsonValue["Id"] = accountName;
1819 asyncResp->res.jsonValue["UserName"] = accountName;
1820 },
1821 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1822 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1823}
1824
1825inline void
1826 handleAccounttDelete(App& app, const crow::Request& req,
1827 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1828 const std::string& username)
1829{
1830 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1831 {
1832 return;
1833 }
1834
1835#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1836 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001837 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001838 return;
1839
1840#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1841 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1842 tempObjPath /= username;
1843 const std::string userPath(tempObjPath);
1844
1845 crow::connections::systemBus->async_method_call(
1846 [asyncResp, username](const boost::system::error_code ec) {
1847 if (ec)
1848 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001849 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
Ed Tanous1ef4c342022-05-12 16:12:36 -07001850 username);
1851 return;
1852 }
1853
1854 messages::accountRemoved(asyncResp->res);
1855 },
1856 "xyz.openbmc_project.User.Manager", userPath,
1857 "xyz.openbmc_project.Object.Delete", "Delete");
1858}
1859
1860inline void
1861 handleAccountPatch(App& app, const crow::Request& req,
1862 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1863 const std::string& username)
1864{
1865 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1866 {
1867 return;
1868 }
1869#ifdef BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1870 // If authentication is disabled, there are no user accounts
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001871 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001872 return;
1873
1874#endif // BMCWEB_INSECURE_DISABLE_AUTHENTICATION
1875 std::optional<std::string> newUserName;
1876 std::optional<std::string> password;
1877 std::optional<bool> enabled;
1878 std::optional<std::string> roleId;
1879 std::optional<bool> locked;
1880
1881 if (req.session == nullptr)
1882 {
1883 messages::internalError(asyncResp->res);
1884 return;
1885 }
1886
1887 Privileges effectiveUserPrivileges =
1888 redfish::getUserPrivileges(req.userRole);
1889 Privileges configureUsers = {"ConfigureUsers"};
1890 bool userHasConfigureUsers =
1891 effectiveUserPrivileges.isSupersetOf(configureUsers);
1892 if (userHasConfigureUsers)
1893 {
1894 // Users with ConfigureUsers can modify for all users
1895 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
1896 newUserName, "Password", password,
1897 "RoleId", roleId, "Enabled", enabled,
1898 "Locked", locked))
1899 {
1900 return;
1901 }
1902 }
1903 else
1904 {
1905 // ConfigureSelf accounts can only modify their own account
1906 if (username != req.session->username)
1907 {
1908 messages::insufficientPrivilege(asyncResp->res);
1909 return;
1910 }
1911
1912 // ConfigureSelf accounts can only modify their password
1913 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
1914 password))
1915 {
1916 return;
1917 }
1918 }
1919
1920 // if user name is not provided in the patch method or if it
1921 // matches the user name in the URI, then we are treating it as
1922 // updating user properties other then username. If username
1923 // provided doesn't match the URI, then we are treating this as
1924 // user rename request.
1925 if (!newUserName || (newUserName.value() == username))
1926 {
1927 updateUserProperties(asyncResp, username, password, enabled, roleId,
1928 locked);
1929 return;
1930 }
1931 crow::connections::systemBus->async_method_call(
1932 [asyncResp, username, password(std::move(password)),
1933 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Patrick Williams59d494e2022-07-22 19:26:55 -05001934 locked](const boost::system::error_code ec, sdbusplus::message_t& m) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001935 if (ec)
1936 {
1937 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
1938 username);
1939 return;
1940 }
1941
1942 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
1943 locked);
1944 },
1945 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1946 "xyz.openbmc_project.User.Manager", "RenameUser", username,
1947 *newUserName);
1948}
1949
Ed Tanous6c51eab2021-06-03 12:30:29 -07001950inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001951{
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001952
Ed Tanous6c51eab2021-06-03 12:30:29 -07001953 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001954 .privileges(redfish::privileges::headAccountService)
1955 .methods(boost::beast::http::verb::head)(
1956 std::bind_front(handleAccountServiceHead, std::ref(app)));
1957
1958 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07001959 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07001960 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001961 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001962
Ed Tanousf5ffd802021-07-19 10:55:33 -07001963 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06001964 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07001965 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001966 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07001967
Ed Tanous6c51eab2021-06-03 12:30:29 -07001968 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001969 .privileges(redfish::privileges::headManagerAccountCollection)
1970 .methods(boost::beast::http::verb::head)(
1971 std::bind_front(handleAccountCollectionHead, std::ref(app)));
1972
1973 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001974 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001975 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001976 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07001977
Ed Tanous6c51eab2021-06-03 12:30:29 -07001978 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07001979 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001980 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001981 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07001982
1983 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001984 .privileges(redfish::privileges::headManagerAccount)
1985 .methods(boost::beast::http::verb::head)(
1986 std::bind_front(handleAccountHead, std::ref(app)));
1987
1988 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07001989 .privileges(redfish::privileges::getManagerAccount)
1990 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001991 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001992
1993 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001994 // TODO this privilege should be using the generated endpoints, but
1995 // because of the special handling of ConfigureSelf, it's not able to
1996 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07001997 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
1998 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001999 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002000
2001 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002002 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002003 .methods(boost::beast::http::verb::delete_)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002004 std::bind_front(handleAccounttDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002005}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002006
Ed Tanous1abe55e2018-09-05 08:30:59 -07002007} // namespace redfish