blob: bf320145a9a278b681df6a97521ffdc560d51b7b [file] [log] [blame]
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001/*
Ed Tanous6be832e2024-09-10 11:44:48 -07002Copyright (c) 2018 Intel Corporation
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010015*/
16#pragma once
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010017
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080018#include "app.hpp"
Gunnar Millsa0735a42024-09-06 12:51:11 -050019#include "boost_formatters.hpp"
Ed Tanous1aa375b2024-04-13 11:51:10 -070020#include "certificate_service.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080021#include "dbus_utility.hpp"
22#include "error_messages.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070023#include "generated/enums/account_service.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080024#include "persistent_data.hpp"
25#include "query.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070026#include "registries/privilege_registry.hpp"
Ed Tanous3281bcf2024-06-25 16:02:05 -070027#include "sessions.hpp"
Ed Tanous1aa375b2024-04-13 11:51:10 -070028#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080029#include "utils/dbus_utils.hpp"
30#include "utils/json_utils.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070031
Ed Tanous1aa375b2024-04-13 11:51:10 -070032#include <boost/url/format.hpp>
33#include <boost/url/url.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070034#include <sdbusplus/asio/property.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020035#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050036
George Liu2b731192023-01-11 16:27:13 +080037#include <array>
Ed Tanous1aa375b2024-04-13 11:51:10 -070038#include <memory>
Abhishek Patelc7229812022-02-01 10:07:15 -060039#include <optional>
Ed Tanous3544d2a2023-08-06 18:12:20 -070040#include <ranges>
Abhishek Patelc7229812022-02-01 10:07:15 -060041#include <string>
George Liu2b731192023-01-11 16:27:13 +080042#include <string_view>
Ed Tanous20fa6a22024-05-20 18:02:58 -070043#include <utility>
Abhishek Patelc7229812022-02-01 10:07:15 -060044#include <vector>
45
Ed Tanous1abe55e2018-09-05 08:30:59 -070046namespace redfish
47{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010048
Ed Tanous23a21a12020-07-25 04:45:05 +000049constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053050 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070051constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053052 "/xyz/openbmc_project/user/ldap/active_directory";
53
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053054constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053055constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
56constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
57constexpr const char* ldapConfigInterface =
58 "xyz.openbmc_project.User.Ldap.Config";
59constexpr const char* ldapCreateInterface =
60 "xyz.openbmc_project.User.Ldap.Create";
61constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053062constexpr const char* ldapPrivMapperInterface =
63 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053064
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060065struct LDAPRoleMapData
66{
67 std::string groupName;
68 std::string privilege;
69};
70
Ratan Gupta6973a582018-12-13 18:25:44 +053071struct LDAPConfigData
72{
Ed Tanous47f29342024-03-19 12:18:06 -070073 std::string uri;
74 std::string bindDN;
75 std::string baseDN;
76 std::string searchScope;
77 std::string serverType;
Ratan Gupta6973a582018-12-13 18:25:44 +053078 bool serviceEnabled = false;
Ed Tanous47f29342024-03-19 12:18:06 -070079 std::string userNameAttribute;
80 std::string groupAttribute;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060081 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053082};
83
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060084inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053085{
86 if (role == "priv-admin")
87 {
88 return "Administrator";
89 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070090 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053091 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053092 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053093 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070094 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053095 {
96 return "Operator";
97 }
98 return "";
99}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600100inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530101{
102 if (role == "Administrator")
103 {
104 return "priv-admin";
105 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700106 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530107 {
108 return "priv-user";
109 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700110 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530111 {
112 return "priv-operator";
113 }
114 return "";
115}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700116
Abhishek Patelc7229812022-02-01 10:07:15 -0600117/**
118 * @brief Maps user group names retrieved from D-Bus object to
119 * Account Types.
120 *
121 * @param[in] userGroups List of User groups
122 * @param[out] res AccountTypes populated
123 *
124 * @return true in case of success, false if UserGroups contains
125 * invalid group name(s).
126 */
127inline bool translateUserGroup(const std::vector<std::string>& userGroups,
128 crow::Response& res)
129{
130 std::vector<std::string> accountTypes;
131 for (const auto& userGroup : userGroups)
132 {
133 if (userGroup == "redfish")
134 {
135 accountTypes.emplace_back("Redfish");
136 accountTypes.emplace_back("WebUI");
137 }
138 else if (userGroup == "ipmi")
139 {
140 accountTypes.emplace_back("IPMI");
141 }
142 else if (userGroup == "ssh")
143 {
Abhishek Patelc7229812022-02-01 10:07:15 -0600144 accountTypes.emplace_back("ManagerConsole");
145 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500146 else if (userGroup == "hostconsole")
147 {
148 // The hostconsole group controls who can access the host console
149 // port via ssh and websocket.
150 accountTypes.emplace_back("HostConsole");
151 }
Abhishek Patelc7229812022-02-01 10:07:15 -0600152 else if (userGroup == "web")
153 {
154 // 'web' is one of the valid groups in the UserGroups property of
155 // the user account in the D-Bus object. This group is currently not
156 // doing anything, and is considered to be equivalent to 'redfish'.
157 // 'redfish' user group is mapped to 'Redfish'and 'WebUI'
158 // AccountTypes, so do nothing here...
159 }
160 else
161 {
Ed Tanous8ece0e42024-01-02 13:16:50 -0800162 // Invalid user group name. Caller throws an exception.
Abhishek Patelc7229812022-02-01 10:07:15 -0600163 return false;
164 }
165 }
166
167 res.jsonValue["AccountTypes"] = std::move(accountTypes);
168 return true;
169}
170
Abhishek Patel58345852022-02-02 08:54:25 -0600171/**
172 * @brief Builds User Groups from the Account Types
173 *
174 * @param[in] asyncResp Async Response
175 * @param[in] accountTypes List of Account Types
176 * @param[out] userGroups List of User Groups mapped from Account Types
177 *
178 * @return true if Account Types mapped to User Groups, false otherwise.
179 */
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400180inline bool getUserGroupFromAccountType(
181 crow::Response& res, const std::vector<std::string>& accountTypes,
182 std::vector<std::string>& userGroups)
Abhishek Patel58345852022-02-02 08:54:25 -0600183{
184 // Need both Redfish and WebUI Account Types to map to 'redfish' User Group
185 bool redfishType = false;
186 bool webUIType = false;
187
188 for (const auto& accountType : accountTypes)
189 {
190 if (accountType == "Redfish")
191 {
192 redfishType = true;
193 }
194 else if (accountType == "WebUI")
195 {
196 webUIType = true;
197 }
198 else if (accountType == "IPMI")
199 {
200 userGroups.emplace_back("ipmi");
201 }
202 else if (accountType == "HostConsole")
203 {
204 userGroups.emplace_back("hostconsole");
205 }
206 else if (accountType == "ManagerConsole")
207 {
208 userGroups.emplace_back("ssh");
209 }
210 else
211 {
212 // Invalid Account Type
213 messages::propertyValueNotInList(res, "AccountTypes", accountType);
214 return false;
215 }
216 }
217
218 // Both Redfish and WebUI Account Types are needed to PATCH
219 if (redfishType ^ webUIType)
220 {
Ed Tanous62598e32023-07-17 17:06:25 -0700221 BMCWEB_LOG_ERROR(
222 "Missing Redfish or WebUI Account Type to set redfish User Group");
Abhishek Patel58345852022-02-02 08:54:25 -0600223 messages::strictAccountTypes(res, "AccountTypes");
224 return false;
225 }
226
227 if (redfishType && webUIType)
228 {
229 userGroups.emplace_back("redfish");
230 }
231
232 return true;
233}
234
235/**
236 * @brief Sets UserGroups property of the user based on the Account Types
237 *
238 * @param[in] accountTypes List of User Account Types
239 * @param[in] asyncResp Async Response
240 * @param[in] dbusObjectPath D-Bus Object Path
241 * @param[in] userSelf true if User is updating OWN Account Types
242 */
243inline void
244 patchAccountTypes(const std::vector<std::string>& accountTypes,
245 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
246 const std::string& dbusObjectPath, bool userSelf)
247{
248 // Check if User is disabling own Redfish Account Type
249 if (userSelf &&
250 (accountTypes.cend() ==
251 std::find(accountTypes.cbegin(), accountTypes.cend(), "Redfish")))
252 {
Ed Tanous62598e32023-07-17 17:06:25 -0700253 BMCWEB_LOG_ERROR(
254 "User disabling OWN Redfish Account Type is not allowed");
Abhishek Patel58345852022-02-02 08:54:25 -0600255 messages::strictAccountTypes(asyncResp->res, "AccountTypes");
256 return;
257 }
258
259 std::vector<std::string> updatedUserGroups;
260 if (!getUserGroupFromAccountType(asyncResp->res, accountTypes,
261 updatedUserGroups))
262 {
263 // Problem in mapping Account Types to User Groups, Error already
264 // logged.
265 return;
266 }
Ginu Georgee93abac2024-06-14 17:35:27 +0530267 setDbusProperty(asyncResp, "AccountTypes",
268 "xyz.openbmc_project.User.Manager", dbusObjectPath,
269 "xyz.openbmc_project.User.Attributes", "UserGroups",
270 updatedUserGroups);
Abhishek Patel58345852022-02-02 08:54:25 -0600271}
272
zhanghch058d1b46d2021-04-01 11:18:24 +0800273inline void userErrorMessageHandler(
274 const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
275 const std::string& newUser, const std::string& username)
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000276{
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000277 if (e == nullptr)
278 {
279 messages::internalError(asyncResp->res);
280 return;
281 }
282
Manojkiran Eda055806b2020-11-03 09:36:28 +0530283 const char* errorMessage = e->name;
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000284 if (strcmp(errorMessage,
285 "xyz.openbmc_project.User.Common.Error.UserNameExists") == 0)
286 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800287 messages::resourceAlreadyExists(asyncResp->res, "ManagerAccount",
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000288 "UserName", newUser);
289 }
290 else if (strcmp(errorMessage, "xyz.openbmc_project.User.Common.Error."
291 "UserNameDoesNotExist") == 0)
292 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800293 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000294 }
Ed Tanousd4d25792020-09-29 15:15:03 -0700295 else if ((strcmp(errorMessage,
296 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
297 0) ||
George Liu0fda0f12021-11-16 10:06:17 +0800298 (strcmp(
299 errorMessage,
300 "xyz.openbmc_project.User.Common.Error.UserNameGroupFail") ==
301 0))
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000302 {
303 messages::propertyValueFormatError(asyncResp->res, newUser, "UserName");
304 }
305 else if (strcmp(errorMessage,
306 "xyz.openbmc_project.User.Common.Error.NoResource") == 0)
307 {
308 messages::createLimitReachedForResource(asyncResp->res);
309 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000310 else
311 {
Gunnar Millsb8ad5832023-10-02 16:26:07 -0500312 BMCWEB_LOG_ERROR("DBUS response error {}", errorMessage);
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000313 messages::internalError(asyncResp->res);
314 }
jayaprakash Mutyala66b5ca72019-08-07 20:26:37 +0000315}
316
Ed Tanous81ce6092020-12-17 16:54:55 +0000317inline void parseLDAPConfigData(nlohmann::json& jsonResponse,
Ed Tanous23a21a12020-07-25 04:45:05 +0000318 const LDAPConfigData& confData,
319 const std::string& ldapType)
Ratan Gupta6973a582018-12-13 18:25:44 +0530320{
Ed Tanous49cc2632024-03-20 12:49:15 -0700321 nlohmann::json::object_t ldap;
Ed Tanous14766872022-03-15 10:44:42 -0700322 ldap["ServiceEnabled"] = confData.serviceEnabled;
Ed Tanous49cc2632024-03-20 12:49:15 -0700323 nlohmann::json::array_t serviceAddresses;
324 serviceAddresses.emplace_back(confData.uri);
325 ldap["ServiceAddresses"] = std::move(serviceAddresses);
326
327 nlohmann::json::object_t authentication;
328 authentication["AuthenticationType"] =
Ed Tanous0ec8b832022-03-14 14:56:47 -0700329 account_service::AuthenticationTypes::UsernameAndPassword;
Ed Tanous49cc2632024-03-20 12:49:15 -0700330 authentication["Username"] = confData.bindDN;
331 authentication["Password"] = nullptr;
332 ldap["Authentication"] = std::move(authentication);
Ed Tanous14766872022-03-15 10:44:42 -0700333
Ed Tanous49cc2632024-03-20 12:49:15 -0700334 nlohmann::json::object_t ldapService;
335 nlohmann::json::object_t searchSettings;
336 nlohmann::json::array_t baseDistinguishedNames;
337 baseDistinguishedNames.emplace_back(confData.baseDN);
Ed Tanous14766872022-03-15 10:44:42 -0700338
Ed Tanous49cc2632024-03-20 12:49:15 -0700339 searchSettings["BaseDistinguishedNames"] =
340 std::move(baseDistinguishedNames);
341 searchSettings["UsernameAttribute"] = confData.userNameAttribute;
342 searchSettings["GroupsAttribute"] = confData.groupAttribute;
343 ldapService["SearchSettings"] = std::move(searchSettings);
344 ldap["LDAPService"] = std::move(ldapService);
345
346 nlohmann::json::array_t roleMapArray;
Ed Tanous9eb808c2022-01-25 10:19:23 -0800347 for (const auto& obj : confData.groupRoleList)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600348 {
Ed Tanous62598e32023-07-17 17:06:25 -0700349 BMCWEB_LOG_DEBUG("Pushing the data groupName={}", obj.second.groupName);
Ed Tanous613dabe2022-07-09 11:17:36 -0700350
Ed Tanous613dabe2022-07-09 11:17:36 -0700351 nlohmann::json::object_t remoteGroup;
352 remoteGroup["RemoteGroup"] = obj.second.groupName;
Jorge Cisneros329f0342022-11-04 16:26:25 +0000353 remoteGroup["LocalRole"] = getRoleIdFromPrivilege(obj.second.privilege);
354 roleMapArray.emplace_back(std::move(remoteGroup));
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600355 }
Ed Tanous49cc2632024-03-20 12:49:15 -0700356
357 ldap["RemoteRoleMapping"] = std::move(roleMapArray);
358
359 jsonResponse[ldapType].update(ldap);
Ratan Gupta6973a582018-12-13 18:25:44 +0530360}
361
362/**
Ratan Gupta06785242019-07-26 22:30:16 +0530363 * @brief validates given JSON input and then calls appropriate method to
364 * create, to delete or to set Rolemapping object based on the given input.
365 *
366 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000367inline void handleRoleMapPatch(
zhanghch058d1b46d2021-04-01 11:18:24 +0800368 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ratan Gupta06785242019-07-26 22:30:16 +0530369 const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
Ed Tanousc1019822024-03-06 12:54:38 -0800370 const std::string& serverType,
371 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>& input)
Ratan Gupta06785242019-07-26 22:30:16 +0530372{
373 for (size_t index = 0; index < input.size(); index++)
374 {
Ed Tanousc1019822024-03-06 12:54:38 -0800375 std::variant<nlohmann::json::object_t, std::nullptr_t>& thisJson =
376 input[index];
377 nlohmann::json::object_t* obj =
378 std::get_if<nlohmann::json::object_t>(&thisJson);
379 if (obj == nullptr)
Ratan Gupta06785242019-07-26 22:30:16 +0530380 {
381 // delete the existing object
382 if (index < roleMapObjData.size())
383 {
384 crow::connections::systemBus->async_method_call(
385 [asyncResp, roleMapObjData, serverType,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800386 index](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400387 if (ec)
388 {
389 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
390 messages::internalError(asyncResp->res);
391 return;
392 }
393 asyncResp->res
394 .jsonValue[serverType]["RemoteRoleMapping"][index] =
395 nullptr;
396 },
Ratan Gupta06785242019-07-26 22:30:16 +0530397 ldapDbusService, roleMapObjData[index].first,
398 "xyz.openbmc_project.Object.Delete", "Delete");
399 }
400 else
401 {
Ed Tanous62598e32023-07-17 17:06:25 -0700402 BMCWEB_LOG_ERROR("Can't delete the object");
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400403 messages::propertyValueTypeError(
404 asyncResp->res, "null",
405 "RemoteRoleMapping/" + std::to_string(index));
Ratan Gupta06785242019-07-26 22:30:16 +0530406 return;
407 }
408 }
Ed Tanousc1019822024-03-06 12:54:38 -0800409 else if (obj->empty())
Ratan Gupta06785242019-07-26 22:30:16 +0530410 {
411 // Don't do anything for the empty objects,parse next json
412 // eg {"RemoteRoleMapping",[{}]}
413 }
414 else
415 {
416 // update/create the object
417 std::optional<std::string> remoteGroup;
418 std::optional<std::string> localRole;
419
Myung Baeafc474a2024-10-09 00:53:29 -0700420 if (!json_util::readJsonObject( //
421 *obj, asyncResp->res, //
422 "LocalRole", localRole, //
423 "RemoteGroup", remoteGroup //
424 ))
Ratan Gupta06785242019-07-26 22:30:16 +0530425 {
426 continue;
427 }
428
429 // Update existing RoleMapping Object
430 if (index < roleMapObjData.size())
431 {
Ed Tanous62598e32023-07-17 17:06:25 -0700432 BMCWEB_LOG_DEBUG("Update Role Map Object");
Ratan Gupta06785242019-07-26 22:30:16 +0530433 // If "RemoteGroup" info is provided
434 if (remoteGroup)
435 {
Ed Tanousd02aad32024-02-13 14:43:34 -0800436 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530437 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800438 std::format("RemoteRoleMapping/{}/RemoteGroup", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530439 ldapDbusService, roleMapObjData[index].first,
440 "xyz.openbmc_project.User.PrivilegeMapperEntry",
441 "GroupName", *remoteGroup);
Ratan Gupta06785242019-07-26 22:30:16 +0530442 }
443
444 // If "LocalRole" info is provided
445 if (localRole)
446 {
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500447 std::string priv = getPrivilegeFromRoleId(*localRole);
448 if (priv.empty())
449 {
450 messages::propertyValueNotInList(
451 asyncResp->res, *localRole,
452 std::format("RemoteRoleMapping/{}/LocalRole",
453 index));
454 return;
455 }
Ed Tanousd02aad32024-02-13 14:43:34 -0800456 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530457 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800458 std::format("RemoteRoleMapping/{}/LocalRole", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530459 ldapDbusService, roleMapObjData[index].first,
460 "xyz.openbmc_project.User.PrivilegeMapperEntry",
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500461 "Privilege", priv);
Ratan Gupta06785242019-07-26 22:30:16 +0530462 }
463 }
464 // Create a new RoleMapping Object.
465 else
466 {
Ed Tanous62598e32023-07-17 17:06:25 -0700467 BMCWEB_LOG_DEBUG(
468 "setRoleMappingProperties: Creating new Object");
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400469 std::string pathString =
470 "RemoteRoleMapping/" + std::to_string(index);
Ratan Gupta06785242019-07-26 22:30:16 +0530471
472 if (!localRole)
473 {
474 messages::propertyMissing(asyncResp->res,
475 pathString + "/LocalRole");
476 continue;
477 }
478 if (!remoteGroup)
479 {
480 messages::propertyMissing(asyncResp->res,
481 pathString + "/RemoteGroup");
482 continue;
483 }
484
485 std::string dbusObjectPath;
486 if (serverType == "ActiveDirectory")
487 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700488 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530489 }
490 else if (serverType == "LDAP")
491 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000492 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530493 }
494
Ed Tanous62598e32023-07-17 17:06:25 -0700495 BMCWEB_LOG_DEBUG("Remote Group={},LocalRole={}", *remoteGroup,
496 *localRole);
Ratan Gupta06785242019-07-26 22:30:16 +0530497
498 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700499 [asyncResp, serverType, localRole,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800500 remoteGroup](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400501 if (ec)
502 {
503 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
504 messages::internalError(asyncResp->res);
505 return;
506 }
507 nlohmann::json& remoteRoleJson =
508 asyncResp->res
509 .jsonValue[serverType]["RemoteRoleMapping"];
510 nlohmann::json::object_t roleMapEntry;
511 roleMapEntry["LocalRole"] = *localRole;
512 roleMapEntry["RemoteGroup"] = *remoteGroup;
513 remoteRoleJson.emplace_back(std::move(roleMapEntry));
514 },
Ratan Gupta06785242019-07-26 22:30:16 +0530515 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700516 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530517 getPrivilegeFromRoleId(std::move(*localRole)));
518 }
519 }
520 }
521}
522
523/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530524 * Function that retrieves all properties for LDAP config object
525 * into JSON
526 */
527template <typename CallbackFunc>
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400528inline void
529 getLDAPConfigData(const std::string& ldapType, CallbackFunc&& callback)
Ratan Gupta6973a582018-12-13 18:25:44 +0530530{
George Liu2b731192023-01-11 16:27:13 +0800531 constexpr std::array<std::string_view, 2> interfaces = {
532 ldapEnableInterface, ldapConfigInterface};
Ratan Gupta6973a582018-12-13 18:25:44 +0530533
George Liu2b731192023-01-11 16:27:13 +0800534 dbus::utility::getDbusObject(
535 ldapConfigObjectName, interfaces,
Ed Tanous8cb2c022024-03-27 16:31:46 -0700536 [callback = std::forward<CallbackFunc>(callback),
Ed Tanousc1019822024-03-06 12:54:38 -0800537 ldapType](const boost::system::error_code& ec,
538 const dbus::utility::MapperGetObject& resp) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400539 if (ec || resp.empty())
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600540 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400541 BMCWEB_LOG_WARNING(
542 "DBUS response error during getting of service name: {}",
543 ec);
544 LDAPConfigData empty{};
545 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600546 return;
547 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400548 std::string service = resp.begin()->first;
549 sdbusplus::message::object_path path(ldapRootObject);
550 dbus::utility::getManagedObjects(
551 service, path,
552 [callback, ldapType](const boost::system::error_code& ec2,
553 const dbus::utility::ManagedObjectType&
554 ldapObjects) mutable {
555 LDAPConfigData confData{};
556 if (ec2)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600557 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400558 callback(false, confData, ldapType);
559 BMCWEB_LOG_WARNING("D-Bus responses error: {}", ec2);
560 return;
561 }
562
563 std::string ldapDbusType;
564 std::string searchString;
565
566 if (ldapType == "LDAP")
567 {
568 ldapDbusType =
569 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
570 searchString = "openldap";
571 }
572 else if (ldapType == "ActiveDirectory")
573 {
574 ldapDbusType =
575 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
576 searchString = "active_directory";
577 }
578 else
579 {
580 BMCWEB_LOG_ERROR(
581 "Can't get the DbusType for the given type={}",
582 ldapType);
583 callback(false, confData, ldapType);
584 return;
585 }
586
587 std::string ldapEnableInterfaceStr = ldapEnableInterface;
588 std::string ldapConfigInterfaceStr = ldapConfigInterface;
589
590 for (const auto& object : ldapObjects)
591 {
592 // let's find the object whose ldap type is equal to the
593 // given type
594 if (object.first.str.find(searchString) ==
595 std::string::npos)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600596 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400597 continue;
598 }
599
600 for (const auto& interface : object.second)
601 {
602 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600603 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400604 // rest of the properties are string.
605 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600606 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400607 if (property.first == "Enabled")
608 {
609 const bool* value =
610 std::get_if<bool>(&property.second);
611 if (value == nullptr)
612 {
613 continue;
614 }
615 confData.serviceEnabled = *value;
616 break;
617 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600618 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400619 }
620 else if (interface.first == ldapConfigInterfaceStr)
621 {
622 for (const auto& property : interface.second)
623 {
624 const std::string* strValue =
625 std::get_if<std::string>(
626 &property.second);
627 if (strValue == nullptr)
628 {
629 continue;
630 }
631 if (property.first == "LDAPServerURI")
632 {
633 confData.uri = *strValue;
634 }
635 else if (property.first == "LDAPBindDN")
636 {
637 confData.bindDN = *strValue;
638 }
639 else if (property.first == "LDAPBaseDN")
640 {
641 confData.baseDN = *strValue;
642 }
643 else if (property.first ==
644 "LDAPSearchScope")
645 {
646 confData.searchScope = *strValue;
647 }
648 else if (property.first ==
649 "GroupNameAttribute")
650 {
651 confData.groupAttribute = *strValue;
652 }
653 else if (property.first ==
654 "UserNameAttribute")
655 {
656 confData.userNameAttribute = *strValue;
657 }
658 else if (property.first == "LDAPType")
659 {
660 confData.serverType = *strValue;
661 }
662 }
663 }
664 else if (
665 interface.first ==
666 "xyz.openbmc_project.User.PrivilegeMapperEntry")
667 {
668 LDAPRoleMapData roleMapData{};
669 for (const auto& property : interface.second)
670 {
671 const std::string* strValue =
672 std::get_if<std::string>(
673 &property.second);
674
675 if (strValue == nullptr)
676 {
677 continue;
678 }
679
680 if (property.first == "GroupName")
681 {
682 roleMapData.groupName = *strValue;
683 }
684 else if (property.first == "Privilege")
685 {
686 roleMapData.privilege = *strValue;
687 }
688 }
689
690 confData.groupRoleList.emplace_back(
691 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600692 }
693 }
694 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400695 callback(true, confData, ldapType);
696 });
George Liu2b731192023-01-11 16:27:13 +0800697 });
Ratan Gupta6973a582018-12-13 18:25:44 +0530698}
699
Ed Tanous6c51eab2021-06-03 12:30:29 -0700700/**
Ed Tanous6c51eab2021-06-03 12:30:29 -0700701 * @brief updates the LDAP server address and updates the
702 json response with the new value.
703 * @param serviceAddressList address to be updated.
704 * @param asyncResp pointer to the JSON response
705 * @param ldapServerElementName Type of LDAP
706 server(openLDAP/ActiveDirectory)
707 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530708
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700709inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700710 const std::vector<std::string>& serviceAddressList,
711 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
712 const std::string& ldapServerElementName,
713 const std::string& ldapConfigObject)
714{
Ginu Georgee93abac2024-06-14 17:35:27 +0530715 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceAddress",
716 ldapDbusService, ldapConfigObject, ldapConfigInterface,
717 "LDAPServerURI", serviceAddressList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700718}
719/**
720 * @brief updates the LDAP Bind DN and updates the
721 json response with the new value.
722 * @param username name of the user which needs to be updated.
723 * @param asyncResp pointer to the JSON response
724 * @param ldapServerElementName Type of LDAP
725 server(openLDAP/ActiveDirectory)
726 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530727
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700728inline void
729 handleUserNamePatch(const std::string& username,
730 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
731 const std::string& ldapServerElementName,
732 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700733{
Ginu Georgee93abac2024-06-14 17:35:27 +0530734 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800735 ldapServerElementName + "/Authentication/Username",
Ginu Georgee93abac2024-06-14 17:35:27 +0530736 ldapDbusService, ldapConfigObject, ldapConfigInterface,
737 "LDAPBindDN", username);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700738}
739
740/**
741 * @brief updates the LDAP password
742 * @param password : ldap password which needs to be updated.
743 * @param asyncResp pointer to the JSON response
744 * @param ldapServerElementName Type of LDAP
745 * server(openLDAP/ActiveDirectory)
746 */
747
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700748inline void
749 handlePasswordPatch(const std::string& password,
750 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
751 const std::string& ldapServerElementName,
752 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700753{
Ginu Georgee93abac2024-06-14 17:35:27 +0530754 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800755 ldapServerElementName + "/Authentication/Password",
Ginu Georgee93abac2024-06-14 17:35:27 +0530756 ldapDbusService, ldapConfigObject, ldapConfigInterface,
757 "LDAPBindDNPassword", password);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700758}
759
760/**
761 * @brief updates the LDAP BaseDN and updates the
762 json response with the new value.
763 * @param baseDNList baseDN list which needs to be updated.
764 * @param asyncResp pointer to the JSON response
765 * @param ldapServerElementName Type of LDAP
766 server(openLDAP/ActiveDirectory)
767 */
768
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700769inline void
770 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
771 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
772 const std::string& ldapServerElementName,
773 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700774{
Ginu Georgee93abac2024-06-14 17:35:27 +0530775 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800776 ldapServerElementName +
777 "/LDAPService/SearchSettings/BaseDistinguishedNames",
Ginu Georgee93abac2024-06-14 17:35:27 +0530778 ldapDbusService, ldapConfigObject, ldapConfigInterface,
779 "LDAPBaseDN", baseDNList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700780}
781/**
782 * @brief updates the LDAP user name attribute and updates the
783 json response with the new value.
784 * @param userNameAttribute attribute to be updated.
785 * @param asyncResp pointer to the JSON response
786 * @param ldapServerElementName Type of LDAP
787 server(openLDAP/ActiveDirectory)
788 */
789
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400790inline void handleUserNameAttrPatch(
791 const std::string& userNameAttribute,
792 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
793 const std::string& ldapServerElementName,
794 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700795{
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400796 setDbusProperty(
797 asyncResp,
798 ldapServerElementName + "LDAPService/SearchSettings/UsernameAttribute",
799 ldapDbusService, ldapConfigObject, ldapConfigInterface,
800 "UserNameAttribute", userNameAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700801}
802/**
803 * @brief updates the LDAP group attribute and updates the
804 json response with the new value.
805 * @param groupsAttribute attribute to be updated.
806 * @param asyncResp pointer to the JSON response
807 * @param ldapServerElementName Type of LDAP
808 server(openLDAP/ActiveDirectory)
809 */
810
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700811inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700812 const std::string& groupsAttribute,
813 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
814 const std::string& ldapServerElementName,
815 const std::string& ldapConfigObject)
816{
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400817 setDbusProperty(
818 asyncResp,
819 ldapServerElementName + "/LDAPService/SearchSettings/GroupsAttribute",
820 ldapDbusService, ldapConfigObject, ldapConfigInterface,
821 "GroupNameAttribute", groupsAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700822}
823/**
824 * @brief updates the LDAP service enable and updates the
825 json response with the new value.
826 * @param input JSON data.
827 * @param asyncResp pointer to the JSON response
828 * @param ldapServerElementName Type of LDAP
829 server(openLDAP/ActiveDirectory)
830 */
831
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700832inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700833 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
834 const std::string& ldapServerElementName,
835 const std::string& ldapConfigObject)
836{
Ginu Georgee93abac2024-06-14 17:35:27 +0530837 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceEnabled",
838 ldapDbusService, ldapConfigObject, ldapEnableInterface,
839 "Enabled", serviceEnabled);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700840}
841
Ed Tanousc1019822024-03-06 12:54:38 -0800842struct AuthMethods
Ed Tanous6c51eab2021-06-03 12:30:29 -0700843{
844 std::optional<bool> basicAuth;
845 std::optional<bool> cookie;
846 std::optional<bool> sessionToken;
847 std::optional<bool> xToken;
848 std::optional<bool> tls;
Ed Tanousc1019822024-03-06 12:54:38 -0800849};
Ed Tanous6c51eab2021-06-03 12:30:29 -0700850
Ed Tanousc1019822024-03-06 12:54:38 -0800851inline void
852 handleAuthMethodsPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
853 const AuthMethods& auth)
854{
855 persistent_data::AuthConfigMethods& authMethodsConfig =
Ed Tanous6c51eab2021-06-03 12:30:29 -0700856 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
857
Ed Tanousc1019822024-03-06 12:54:38 -0800858 if (auth.basicAuth)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700859 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700860 if constexpr (!BMCWEB_BASIC_AUTH)
861 {
862 messages::actionNotSupported(
863 asyncResp->res,
864 "Setting BasicAuth when basic-auth feature is disabled");
865 return;
866 }
867
Ed Tanousc1019822024-03-06 12:54:38 -0800868 authMethodsConfig.basic = *auth.basicAuth;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700869 }
870
Ed Tanousc1019822024-03-06 12:54:38 -0800871 if (auth.cookie)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700872 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700873 if constexpr (!BMCWEB_COOKIE_AUTH)
874 {
875 messages::actionNotSupported(
876 asyncResp->res,
877 "Setting Cookie when cookie-auth feature is disabled");
878 return;
879 }
Ed Tanousc1019822024-03-06 12:54:38 -0800880 authMethodsConfig.cookie = *auth.cookie;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700881 }
882
Ed Tanousc1019822024-03-06 12:54:38 -0800883 if (auth.sessionToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700884 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700885 if constexpr (!BMCWEB_SESSION_AUTH)
886 {
887 messages::actionNotSupported(
888 asyncResp->res,
889 "Setting SessionToken when session-auth feature is disabled");
890 return;
891 }
Ed Tanousc1019822024-03-06 12:54:38 -0800892 authMethodsConfig.sessionToken = *auth.sessionToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700893 }
894
Ed Tanousc1019822024-03-06 12:54:38 -0800895 if (auth.xToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700896 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700897 if constexpr (!BMCWEB_XTOKEN_AUTH)
898 {
899 messages::actionNotSupported(
900 asyncResp->res,
901 "Setting XToken when xtoken-auth feature is disabled");
902 return;
903 }
Ed Tanousc1019822024-03-06 12:54:38 -0800904 authMethodsConfig.xtoken = *auth.xToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700905 }
906
Ed Tanousc1019822024-03-06 12:54:38 -0800907 if (auth.tls)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700908 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700909 if constexpr (!BMCWEB_MUTUAL_TLS_AUTH)
910 {
911 messages::actionNotSupported(
912 asyncResp->res,
913 "Setting TLS when mutual-tls-auth feature is disabled");
914 return;
915 }
Ed Tanousc1019822024-03-06 12:54:38 -0800916 authMethodsConfig.tls = *auth.tls;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700917 }
918
919 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
920 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
921 !authMethodsConfig.tls)
922 {
923 // Do not allow user to disable everything
924 messages::actionNotSupported(asyncResp->res,
925 "of disabling all available methods");
926 return;
927 }
928
929 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
930 authMethodsConfig);
931 // Save configuration immediately
932 persistent_data::getConfig().writeData();
933
934 messages::success(asyncResp->res);
935}
936
937/**
938 * @brief Get the required values from the given JSON, validates the
939 * value and create the LDAP config object.
940 * @param input JSON data
941 * @param asyncResp pointer to the JSON response
942 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
943 */
944
Ed Tanous10cb44f2024-04-11 13:05:20 -0700945struct LdapPatchParams
946{
947 std::optional<std::string> authType;
948 std::optional<std::vector<std::string>> serviceAddressList;
949 std::optional<bool> serviceEnabled;
950 std::optional<std::vector<std::string>> baseDNList;
951 std::optional<std::string> userNameAttribute;
952 std::optional<std::string> groupsAttribute;
953 std::optional<std::string> userName;
954 std::optional<std::string> password;
955 std::optional<
956 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>
957 remoteRoleMapData;
958};
959
960inline void handleLDAPPatch(LdapPatchParams&& input,
Ed Tanous6c51eab2021-06-03 12:30:29 -0700961 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
962 const std::string& serverType)
963{
964 std::string dbusObjectPath;
965 if (serverType == "ActiveDirectory")
966 {
967 dbusObjectPath = adConfigObject;
968 }
969 else if (serverType == "LDAP")
970 {
971 dbusObjectPath = ldapConfigObjectName;
972 }
973 else
974 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700975 BMCWEB_LOG_ERROR("serverType wasn't AD or LDAP but was {}????",
976 serverType);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700977 return;
978 }
979
Ed Tanous10cb44f2024-04-11 13:05:20 -0700980 if (input.authType && *input.authType != "UsernameAndPassword")
Ed Tanous6c51eab2021-06-03 12:30:29 -0700981 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700982 messages::propertyValueNotInList(asyncResp->res, *input.authType,
Ed Tanousc1019822024-03-06 12:54:38 -0800983 "AuthenticationType");
984 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700985 }
Ed Tanousc1019822024-03-06 12:54:38 -0800986
Ed Tanous10cb44f2024-04-11 13:05:20 -0700987 if (input.serviceAddressList)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700988 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700989 if (input.serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530990 {
Ed Tanouse2616cc2022-06-27 12:45:55 -0700991 messages::propertyValueNotInList(
Ed Tanous10cb44f2024-04-11 13:05:20 -0700992 asyncResp->res, *input.serviceAddressList, "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +0000993 return;
994 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700995 }
Ed Tanous10cb44f2024-04-11 13:05:20 -0700996 if (input.baseDNList)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700997 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700998 if (input.baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +0530999 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001000 messages::propertyValueNotInList(asyncResp->res, *input.baseDNList,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001001 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +05301002 return;
1003 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001004 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301005
Ed Tanous6c51eab2021-06-03 12:30:29 -07001006 // nothing to update, then return
Ed Tanous10cb44f2024-04-11 13:05:20 -07001007 if (!input.userName && !input.password && !input.serviceAddressList &&
1008 !input.baseDNList && !input.userNameAttribute &&
1009 !input.groupsAttribute && !input.serviceEnabled &&
1010 !input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001011 {
1012 return;
1013 }
1014
1015 // Get the existing resource first then keep modifying
1016 // whenever any property gets updated.
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001017 getLDAPConfigData(serverType, [asyncResp, input = std::move(input),
1018 dbusObjectPath = std::move(dbusObjectPath)](
1019 bool success,
1020 const LDAPConfigData& confData,
1021 const std::string& serverT) mutable {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001022 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301023 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001024 messages::internalError(asyncResp->res);
1025 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301026 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001027 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1028 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301029 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001030 // Disable the service first and update the rest of
1031 // the properties.
1032 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301033 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001034
Ed Tanous10cb44f2024-04-11 13:05:20 -07001035 if (input.serviceAddressList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301036 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001037 handleServiceAddressPatch(*input.serviceAddressList, asyncResp,
1038 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301039 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001040 if (input.userName)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001041 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001042 handleUserNamePatch(*input.userName, asyncResp, serverT,
1043 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001044 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001045 if (input.password)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001046 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001047 handlePasswordPatch(*input.password, asyncResp, serverT,
1048 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001049 }
1050
Ed Tanous10cb44f2024-04-11 13:05:20 -07001051 if (input.baseDNList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301052 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001053 handleBaseDNPatch(*input.baseDNList, asyncResp, serverT,
1054 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001055 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001056 if (input.userNameAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001057 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001058 handleUserNameAttrPatch(*input.userNameAttribute, asyncResp,
1059 serverT, dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001060 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001061 if (input.groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001062 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001063 handleGroupNameAttrPatch(*input.groupsAttribute, asyncResp, serverT,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001064 dbusObjectPath);
1065 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001066 if (input.serviceEnabled)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001067 {
1068 // if user has given the value as true then enable
1069 // the service. if user has given false then no-op
1070 // as service is already stopped.
Ed Tanous10cb44f2024-04-11 13:05:20 -07001071 if (*input.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301072 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001073 handleServiceEnablePatch(*input.serviceEnabled, asyncResp,
1074 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301075 }
1076 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001077 else
1078 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001079 // if user has not given the service enabled value
1080 // then revert it to the same state as it was
1081 // before.
1082 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1083 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001084 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001085
Ed Tanous10cb44f2024-04-11 13:05:20 -07001086 if (input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001087 {
1088 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001089 *input.remoteRoleMapData);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001090 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001091 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001092}
1093
Abhishek Patel58345852022-02-02 08:54:25 -06001094inline void updateUserProperties(
1095 std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& username,
1096 const std::optional<std::string>& password,
1097 const std::optional<bool>& enabled,
1098 const std::optional<std::string>& roleId, const std::optional<bool>& locked,
Ravi Tejae518ef32024-05-16 10:33:08 -05001099 std::optional<std::vector<std::string>> accountTypes, bool userSelf,
1100 const std::shared_ptr<persistent_data::UserSession>& session)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001101{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301102 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1103 tempObjPath /= username;
1104 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001105
1106 dbus::utility::checkDbusPathExists(
Ravi Tejae518ef32024-05-16 10:33:08 -05001107 dbusObjectPath,
1108 [dbusObjectPath, username, password, roleId, enabled, locked,
1109 accountTypes(std::move(accountTypes)), userSelf, session,
1110 asyncResp{std::move(asyncResp)}](int rc) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001111 if (rc <= 0)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001112 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001113 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1114 username);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001115 return;
1116 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001117
1118 if (password)
Ed Tanous618c14b2022-06-30 17:44:25 -07001119 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001120 int retval = pamUpdatePassword(username, *password);
1121
1122 if (retval == PAM_USER_UNKNOWN)
1123 {
1124 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1125 username);
1126 }
1127 else if (retval == PAM_AUTHTOK_ERR)
1128 {
1129 // If password is invalid
1130 messages::propertyValueFormatError(asyncResp->res, nullptr,
1131 "Password");
1132 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
1133 }
1134 else if (retval != PAM_SUCCESS)
1135 {
1136 messages::internalError(asyncResp->res);
1137 return;
1138 }
1139 else
1140 {
1141 // Remove existing sessions of the user when password
1142 // changed
1143 persistent_data::SessionStore::getInstance()
1144 .removeSessionsByUsernameExceptSession(username,
1145 session);
1146 messages::success(asyncResp->res);
1147 }
Ed Tanous618c14b2022-06-30 17:44:25 -07001148 }
1149
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001150 if (enabled)
Patrick Williams5a39f772023-10-20 11:20:21 -05001151 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001152 setDbusProperty(
1153 asyncResp, "Enabled", "xyz.openbmc_project.User.Manager",
1154 dbusObjectPath, "xyz.openbmc_project.User.Attributes",
1155 "UserEnabled", *enabled);
Ed Tanous618c14b2022-06-30 17:44:25 -07001156 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001157
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001158 if (roleId)
Abhishek Patel58345852022-02-02 08:54:25 -06001159 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001160 std::string priv = getPrivilegeFromRoleId(*roleId);
1161 if (priv.empty())
1162 {
1163 messages::propertyValueNotInList(asyncResp->res, true,
1164 "Locked");
1165 return;
1166 }
1167 setDbusProperty(
1168 asyncResp, "RoleId", "xyz.openbmc_project.User.Manager",
1169 dbusObjectPath, "xyz.openbmc_project.User.Attributes",
1170 "UserPrivilege", priv);
Abhishek Patel58345852022-02-02 08:54:25 -06001171 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001172
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001173 if (locked)
1174 {
1175 // admin can unlock the account which is locked by
1176 // successive authentication failures but admin should
1177 // not be allowed to lock an account.
1178 if (*locked)
1179 {
1180 messages::propertyValueNotInList(asyncResp->res, "true",
1181 "Locked");
1182 return;
1183 }
1184 setDbusProperty(
1185 asyncResp, "Locked", "xyz.openbmc_project.User.Manager",
1186 dbusObjectPath, "xyz.openbmc_project.User.Attributes",
1187 "UserLockedForFailedAttempt", *locked);
1188 }
1189
1190 if (accountTypes)
1191 {
1192 patchAccountTypes(*accountTypes, asyncResp, dbusObjectPath,
1193 userSelf);
1194 }
1195 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001196}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001197
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001198inline void handleAccountServiceHead(
1199 App& app, const crow::Request& req,
1200 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001201{
1202 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1203 {
1204 return;
1205 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001206 asyncResp->res.addHeader(
1207 boost::beast::http::field::link,
1208 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1209}
1210
1211inline void
Ed Tanous1aa375b2024-04-13 11:51:10 -07001212 getClientCertificates(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1213 const nlohmann::json::json_pointer& keyLocation)
1214{
1215 boost::urls::url url(
1216 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates");
1217 std::array<std::string_view, 1> interfaces = {
1218 "xyz.openbmc_project.Certs.Certificate"};
1219 std::string path = "/xyz/openbmc_project/certs/authority/truststore";
1220
1221 collection_util::getCollectionToKey(asyncResp, url, interfaces, path,
1222 keyLocation);
1223}
1224
1225inline void handleAccountServiceClientCertificatesInstanceHead(
1226 App& app, const crow::Request& req,
1227 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1228 const std::string& /*id*/)
1229{
1230 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1231 {
1232 return;
1233 }
1234
1235 asyncResp->res.addHeader(
1236 boost::beast::http::field::link,
1237 "</redfish/v1/JsonSchemas/Certificate/Certificate.json>; rel=describedby");
1238}
1239
1240inline void handleAccountServiceClientCertificatesInstanceGet(
1241 App& app, const crow::Request& req,
1242 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1243{
1244 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1245 {
1246 return;
1247 }
1248 BMCWEB_LOG_DEBUG("ClientCertificate Certificate ID={}", id);
1249 const boost::urls::url certURL = boost::urls::format(
1250 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/{}",
1251 id);
1252 std::string objPath =
1253 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1254 getCertificateProperties(
1255 asyncResp, objPath,
1256 "xyz.openbmc_project.Certs.Manager.Authority.Truststore", id, certURL,
1257 "Client Certificate");
1258}
1259
1260inline void handleAccountServiceClientCertificatesHead(
1261 App& app, const crow::Request& req,
1262 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1263{
1264 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1265 {
1266 return;
1267 }
1268
1269 asyncResp->res.addHeader(
1270 boost::beast::http::field::link,
1271 "</redfish/v1/JsonSchemas/CertificateCollection/CertificateCollection.json>; rel=describedby");
1272}
1273
1274inline void handleAccountServiceClientCertificatesGet(
1275 App& app, const crow::Request& req,
1276 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1277{
1278 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1279 {
1280 return;
1281 }
1282 getClientCertificates(asyncResp, "/Members"_json_pointer);
1283}
1284
Ed Tanous3ce36882024-06-09 10:58:16 -07001285using account_service::CertificateMappingAttribute;
1286using persistent_data::MTLSCommonNameParseMode;
1287inline CertificateMappingAttribute
1288 getCertificateMapping(MTLSCommonNameParseMode parse)
1289{
1290 switch (parse)
1291 {
1292 case MTLSCommonNameParseMode::CommonName:
1293 {
1294 return CertificateMappingAttribute::CommonName;
1295 }
1296 break;
1297 case MTLSCommonNameParseMode::Whole:
1298 {
1299 return CertificateMappingAttribute::Whole;
1300 }
1301 break;
1302 case MTLSCommonNameParseMode::UserPrincipalName:
1303 {
1304 return CertificateMappingAttribute::UserPrincipalName;
1305 }
1306 break;
1307
1308 case MTLSCommonNameParseMode::Meta:
1309 {
1310 if constexpr (BMCWEB_META_TLS_COMMON_NAME_PARSING)
1311 {
1312 return CertificateMappingAttribute::CommonName;
1313 }
1314 }
1315 break;
1316 default:
1317 {
1318 return CertificateMappingAttribute::Invalid;
1319 }
1320 break;
1321 }
1322}
1323
Ed Tanous1aa375b2024-04-13 11:51:10 -07001324inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001325 handleAccountServiceGet(App& app, const crow::Request& req,
1326 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1327{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001328 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1329 {
1330 return;
1331 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001332
1333 if (req.session == nullptr)
1334 {
1335 messages::internalError(asyncResp->res);
1336 return;
1337 }
1338
Ed Tanousc1019822024-03-06 12:54:38 -08001339 const persistent_data::AuthConfigMethods& authMethodsConfig =
1340 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1341
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001342 asyncResp->res.addHeader(
1343 boost::beast::http::field::link,
1344 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1345
Ed Tanous1ef4c342022-05-12 16:12:36 -07001346 nlohmann::json& json = asyncResp->res.jsonValue;
1347 json["@odata.id"] = "/redfish/v1/AccountService";
Ravi Teja482a69e2024-04-22 06:56:13 -05001348 json["@odata.type"] = "#AccountService.v1_15_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001349 json["Id"] = "AccountService";
1350 json["Name"] = "Account Service";
1351 json["Description"] = "Account Service";
1352 json["ServiceEnabled"] = true;
1353 json["MaxPasswordLength"] = 20;
1354 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1355 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
Ravi Teja482a69e2024-04-22 06:56:13 -05001356 json["HTTPBasicAuth"] = authMethodsConfig.basic
1357 ? account_service::BasicAuthState::Enabled
1358 : account_service::BasicAuthState::Disabled;
1359
1360 nlohmann::json::array_t allowed;
1361 allowed.emplace_back(account_service::BasicAuthState::Enabled);
1362 allowed.emplace_back(account_service::BasicAuthState::Disabled);
1363 json["HTTPBasicAuth@AllowableValues"] = std::move(allowed);
1364
Ed Tanous1aa375b2024-04-13 11:51:10 -07001365 nlohmann::json::object_t clientCertificate;
1366 clientCertificate["Enabled"] = authMethodsConfig.tls;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001367 clientCertificate["RespondToUnauthenticatedClients"] =
1368 !authMethodsConfig.tlsStrict;
Ed Tanous3ce36882024-06-09 10:58:16 -07001369
1370 using account_service::CertificateMappingAttribute;
1371
1372 CertificateMappingAttribute mapping =
1373 getCertificateMapping(authMethodsConfig.mTLSCommonNameParsingMode);
1374 if (mapping == CertificateMappingAttribute::Invalid)
1375 {
1376 messages::internalError(asyncResp->res);
1377 }
1378 else
1379 {
1380 clientCertificate["CertificateMappingAttribute"] = mapping;
1381 }
Ed Tanous1aa375b2024-04-13 11:51:10 -07001382 nlohmann::json::object_t certificates;
1383 certificates["@odata.id"] =
1384 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates";
1385 certificates["@odata.type"] =
1386 "#CertificateCollection.CertificateCollection";
1387 clientCertificate["Certificates"] = std::move(certificates);
1388 json["MultiFactorAuth"]["ClientCertificate"] = std::move(clientCertificate);
1389
1390 getClientCertificates(
1391 asyncResp,
1392 "/MultiFactorAuth/ClientCertificate/Certificates/Members"_json_pointer);
1393
Ed Tanous1ef4c342022-05-12 16:12:36 -07001394 json["Oem"]["OpenBMC"]["@odata.type"] =
Ed Tanous5b5574a2022-09-26 19:53:36 -07001395 "#OpenBMCAccountService.v1_0_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001396 json["Oem"]["OpenBMC"]["@odata.id"] =
1397 "/redfish/v1/AccountService#/Oem/OpenBMC";
1398 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1399 authMethodsConfig.basic;
1400 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1401 authMethodsConfig.sessionToken;
1402 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1403 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1404 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1405
1406 // /redfish/v1/AccountService/LDAP/Certificates is something only
1407 // ConfigureManager can access then only display when the user has
1408 // permissions ConfigureManager
1409 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001410 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001411
1412 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1413 effectiveUserPrivileges))
1414 {
1415 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1416 "/redfish/v1/AccountService/LDAP/Certificates";
1417 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001418 sdbusplus::asio::getAllProperties(
1419 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1420 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.AccountPolicy",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001421 [asyncResp](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001422 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001423 if (ec)
1424 {
1425 messages::internalError(asyncResp->res);
1426 return;
1427 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001428
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001429 BMCWEB_LOG_DEBUG("Got {} properties for AccountService",
1430 propertiesList.size());
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001431
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001432 const uint8_t* minPasswordLength = nullptr;
1433 const uint32_t* accountUnlockTimeout = nullptr;
1434 const uint16_t* maxLoginAttemptBeforeLockout = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001435
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001436 const bool success = sdbusplus::unpackPropertiesNoThrow(
1437 dbus_utils::UnpackErrorPrinter(), propertiesList,
1438 "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout",
1439 accountUnlockTimeout, "MaxLoginAttemptBeforeLockout",
1440 maxLoginAttemptBeforeLockout);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001441
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001442 if (!success)
1443 {
1444 messages::internalError(asyncResp->res);
1445 return;
1446 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001447
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001448 if (minPasswordLength != nullptr)
1449 {
1450 asyncResp->res.jsonValue["MinPasswordLength"] =
1451 *minPasswordLength;
1452 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001453
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001454 if (accountUnlockTimeout != nullptr)
1455 {
1456 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1457 *accountUnlockTimeout;
1458 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001459
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001460 if (maxLoginAttemptBeforeLockout != nullptr)
1461 {
1462 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1463 *maxLoginAttemptBeforeLockout;
1464 }
1465 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001466
Ed Tanous02cad962022-06-30 16:50:15 -07001467 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001468 const std::string& ldapType) {
1469 if (!success)
1470 {
1471 return;
1472 }
1473 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1474 };
1475
1476 getLDAPConfigData("LDAP", callback);
1477 getLDAPConfigData("ActiveDirectory", callback);
1478}
1479
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001480inline void handleCertificateMappingAttributePatch(
1481 crow::Response& res, const std::string& certMapAttribute)
Ed Tanous3ce36882024-06-09 10:58:16 -07001482{
1483 MTLSCommonNameParseMode parseMode =
1484 persistent_data::getMTLSCommonNameParseMode(certMapAttribute);
1485 if (parseMode == MTLSCommonNameParseMode::Invalid)
1486 {
1487 messages::propertyValueNotInList(res, "CertificateMappingAttribute",
1488 certMapAttribute);
1489 return;
1490 }
1491
1492 persistent_data::AuthConfigMethods& authMethodsConfig =
1493 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1494 authMethodsConfig.mTLSCommonNameParsingMode = parseMode;
1495}
1496
Ed Tanous3281bcf2024-06-25 16:02:05 -07001497inline void handleRespondToUnauthenticatedClientsPatch(
1498 App& app, const crow::Request& req, crow::Response& res,
1499 bool respondToUnauthenticatedClients)
1500{
1501 if (req.session != nullptr)
1502 {
1503 // Sanity check. If the user isn't currently authenticated with mutual
1504 // TLS, they very likely are about to permanently lock themselves out.
1505 // Make sure they're using mutual TLS before allowing locking.
1506 if (req.session->sessionType != persistent_data::SessionType::MutualTLS)
1507 {
1508 messages::propertyValueExternalConflict(
1509 res,
1510 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
1511 respondToUnauthenticatedClients);
1512 return;
1513 }
1514 }
1515
1516 persistent_data::AuthConfigMethods& authMethodsConfig =
1517 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1518
1519 // Change the settings
1520 authMethodsConfig.tlsStrict = !respondToUnauthenticatedClients;
1521
1522 // Write settings to disk
1523 persistent_data::getConfig().writeData();
1524
1525 // Trigger a reload, to apply the new settings to new connections
1526 app.loadCertificate();
1527}
1528
Ed Tanous1ef4c342022-05-12 16:12:36 -07001529inline void handleAccountServicePatch(
1530 App& app, const crow::Request& req,
1531 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1532{
1533 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1534 {
1535 return;
1536 }
1537 std::optional<uint32_t> unlockTimeout;
1538 std::optional<uint16_t> lockoutThreshold;
1539 std::optional<uint8_t> minPasswordLength;
1540 std::optional<uint16_t> maxPasswordLength;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001541 LdapPatchParams ldapObject;
Ed Tanous3ce36882024-06-09 10:58:16 -07001542 std::optional<std::string> certificateMappingAttribute;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001543 std::optional<bool> respondToUnauthenticatedClients;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001544 LdapPatchParams activeDirectoryObject;
Ed Tanousc1019822024-03-06 12:54:38 -08001545 AuthMethods auth;
Ravi Teja482a69e2024-04-22 06:56:13 -05001546 std::optional<std::string> httpBasicAuth;
Ed Tanous3ce36882024-06-09 10:58:16 -07001547
Myung Baeafc474a2024-10-09 00:53:29 -07001548 if (!json_util::readJsonPatch( //
1549 req, asyncResp->res, //
1550 "AccountLockoutDuration", unlockTimeout, //
1551 "AccountLockoutThreshold", lockoutThreshold, //
1552 "ActiveDirectory/Authentication/AuthenticationType",
1553 activeDirectoryObject.authType, //
1554 "ActiveDirectory/Authentication/Password",
1555 activeDirectoryObject.password, //
1556 "ActiveDirectory/Authentication/Username",
1557 activeDirectoryObject.userName, //
1558 "ActiveDirectory/LDAPService/SearchSettings/BaseDistinguishedNames",
1559 activeDirectoryObject.baseDNList, //
1560 "ActiveDirectory/LDAPService/SearchSettings/GroupsAttribute",
1561 activeDirectoryObject.groupsAttribute, //
1562 "ActiveDirectory/LDAPService/SearchSettings/UsernameAttribute",
1563 activeDirectoryObject.userNameAttribute, //
1564 "ActiveDirectory/RemoteRoleMapping",
1565 activeDirectoryObject.remoteRoleMapData, //
1566 "ActiveDirectory/ServiceAddresses",
1567 activeDirectoryObject.serviceAddressList, //
1568 "ActiveDirectory/ServiceEnabled",
1569 activeDirectoryObject.serviceEnabled, //
1570 "HTTPBasicAuth", httpBasicAuth, //
1571 "LDAP/Authentication/AuthenticationType", ldapObject.authType, //
1572 "LDAP/Authentication/Password", ldapObject.password, //
1573 "LDAP/Authentication/Username", ldapObject.userName, //
1574 "LDAP/LDAPService/SearchSettings/BaseDistinguishedNames",
1575 ldapObject.baseDNList, //
1576 "LDAP/LDAPService/SearchSettings/GroupsAttribute",
1577 ldapObject.groupsAttribute, //
1578 "LDAP/LDAPService/SearchSettings/UsernameAttribute",
1579 ldapObject.userNameAttribute, //
1580 "LDAP/RemoteRoleMapping", ldapObject.remoteRoleMapData, //
1581 "LDAP/ServiceAddresses", ldapObject.serviceAddressList, //
1582 "LDAP/ServiceEnabled", ldapObject.serviceEnabled, //
1583 "MaxPasswordLength", maxPasswordLength, //
1584 "MinPasswordLength", minPasswordLength, //
1585 "MultiFactorAuth/ClientCertificate/CertificateMappingAttribute",
1586 certificateMappingAttribute, //
1587 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
1588 respondToUnauthenticatedClients, //
1589 "Oem/OpenBMC/AuthMethods/BasicAuth", auth.basicAuth, //
1590 "Oem/OpenBMC/AuthMethods/Cookie", auth.cookie, //
1591 "Oem/OpenBMC/AuthMethods/SessionToken", auth.sessionToken, //
1592 "Oem/OpenBMC/AuthMethods/TLS", auth.tls, //
1593 "Oem/OpenBMC/AuthMethods/XToken", auth.xToken //
1594 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001595 {
1596 return;
1597 }
1598
Ravi Teja482a69e2024-04-22 06:56:13 -05001599 if (httpBasicAuth)
1600 {
1601 if (*httpBasicAuth == "Enabled")
1602 {
1603 auth.basicAuth = true;
1604 }
1605 else if (*httpBasicAuth == "Disabled")
1606 {
1607 auth.basicAuth = false;
1608 }
1609 else
1610 {
1611 messages::propertyValueNotInList(asyncResp->res, "HttpBasicAuth",
1612 *httpBasicAuth);
1613 }
1614 }
1615
Ed Tanous3281bcf2024-06-25 16:02:05 -07001616 if (respondToUnauthenticatedClients)
1617 {
1618 handleRespondToUnauthenticatedClientsPatch(
1619 app, req, asyncResp->res, *respondToUnauthenticatedClients);
1620 }
1621
Ed Tanous3ce36882024-06-09 10:58:16 -07001622 if (certificateMappingAttribute)
1623 {
1624 handleCertificateMappingAttributePatch(asyncResp->res,
1625 *certificateMappingAttribute);
1626 }
1627
Ed Tanous1ef4c342022-05-12 16:12:36 -07001628 if (minPasswordLength)
1629 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001630 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301631 asyncResp, "MinPasswordLength", "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001632 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001633 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
Ginu Georgee93abac2024-06-14 17:35:27 +05301634 *minPasswordLength);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001635 }
1636
1637 if (maxPasswordLength)
1638 {
1639 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1640 }
1641
Ed Tanous10cb44f2024-04-11 13:05:20 -07001642 handleLDAPPatch(std::move(activeDirectoryObject), asyncResp,
1643 "ActiveDirectory");
1644 handleLDAPPatch(std::move(ldapObject), asyncResp, "LDAP");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001645
Ed Tanousc1019822024-03-06 12:54:38 -08001646 handleAuthMethodsPatch(asyncResp, auth);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001647
Ed Tanous1ef4c342022-05-12 16:12:36 -07001648 if (unlockTimeout)
1649 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001650 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301651 asyncResp, "AccountLockoutDuration",
1652 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001653 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
Ed Tanous1ef4c342022-05-12 16:12:36 -07001654 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
Ginu Georgee93abac2024-06-14 17:35:27 +05301655 *unlockTimeout);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001656 }
1657 if (lockoutThreshold)
1658 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001659 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301660 asyncResp, "AccountLockoutThreshold",
1661 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001662 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001663 "xyz.openbmc_project.User.AccountPolicy",
Ginu Georgee93abac2024-06-14 17:35:27 +05301664 "MaxLoginAttemptBeforeLockout", *lockoutThreshold);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001665 }
1666}
1667
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001668inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001669 App& app, const crow::Request& req,
1670 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1671{
1672 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1673 {
1674 return;
1675 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001676 asyncResp->res.addHeader(
1677 boost::beast::http::field::link,
1678 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1679}
1680
1681inline void handleAccountCollectionGet(
1682 App& app, const crow::Request& req,
1683 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1684{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001685 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1686 {
1687 return;
1688 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001689
1690 if (req.session == nullptr)
1691 {
1692 messages::internalError(asyncResp->res);
1693 return;
1694 }
1695
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001696 asyncResp->res.addHeader(
1697 boost::beast::http::field::link,
1698 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001699
1700 asyncResp->res.jsonValue["@odata.id"] =
1701 "/redfish/v1/AccountService/Accounts";
1702 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1703 "ManagerAccountCollection";
1704 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1705 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1706
1707 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001708 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001709
1710 std::string thisUser;
1711 if (req.session)
1712 {
1713 thisUser = req.session->username;
1714 }
George Liu5eb468d2023-06-20 17:03:24 +08001715 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
1716 dbus::utility::getManagedObjects(
1717 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001718 [asyncResp, thisUser, effectiveUserPrivileges](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001719 const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001720 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001721 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001722 {
1723 messages::internalError(asyncResp->res);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001724 return;
1725 }
1726
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001727 bool userCanSeeAllAccounts =
1728 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1729
1730 bool userCanSeeSelf =
1731 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1732
1733 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1734 memberArray = nlohmann::json::array();
1735
1736 for (const auto& userpath : users)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001737 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001738 std::string user = userpath.first.filename();
1739 if (user.empty())
1740 {
1741 messages::internalError(asyncResp->res);
1742 BMCWEB_LOG_ERROR("Invalid firmware ID");
1743
1744 return;
1745 }
1746
1747 // As clarified by Redfish here:
1748 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1749 // Users without ConfigureUsers, only see their own
1750 // account. Users with ConfigureUsers, see all
1751 // accounts.
1752 if (userCanSeeAllAccounts ||
1753 (thisUser == user && userCanSeeSelf))
1754 {
1755 nlohmann::json::object_t member;
1756 member["@odata.id"] = boost::urls::format(
1757 "/redfish/v1/AccountService/Accounts/{}", user);
1758 memberArray.emplace_back(std::move(member));
1759 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001760 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001761 asyncResp->res.jsonValue["Members@odata.count"] =
1762 memberArray.size();
1763 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001764}
1765
Ninad Palsule97e90da2023-05-17 14:04:52 -05001766inline void processAfterCreateUser(
1767 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1768 const std::string& username, const std::string& password,
1769 const boost::system::error_code& ec, sdbusplus::message_t& m)
1770{
1771 if (ec)
1772 {
1773 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1774 return;
1775 }
1776
1777 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1778 {
1779 // At this point we have a user that's been
1780 // created, but the password set
1781 // failed.Something is wrong, so delete the user
1782 // that we've already created
1783 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1784 tempObjPath /= username;
1785 const std::string userPath(tempObjPath);
1786
1787 crow::connections::systemBus->async_method_call(
1788 [asyncResp, password](const boost::system::error_code& ec3) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001789 if (ec3)
1790 {
1791 messages::internalError(asyncResp->res);
1792 return;
1793 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001794
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001795 // If password is invalid
1796 messages::propertyValueFormatError(asyncResp->res, nullptr,
1797 "Password");
1798 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001799 "xyz.openbmc_project.User.Manager", userPath,
1800 "xyz.openbmc_project.Object.Delete", "Delete");
1801
Ed Tanous62598e32023-07-17 17:06:25 -07001802 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
Ninad Palsule97e90da2023-05-17 14:04:52 -05001803 return;
1804 }
1805
1806 messages::created(asyncResp->res);
1807 asyncResp->res.addHeader("Location",
1808 "/redfish/v1/AccountService/Accounts/" + username);
1809}
1810
1811inline void processAfterGetAllGroups(
1812 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1813 const std::string& username, const std::string& password,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001814 const std::string& roleId, bool enabled,
Ninad Palsule9ba73932023-06-01 16:38:57 -05001815 std::optional<std::vector<std::string>> accountTypes,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001816 const std::vector<std::string>& allGroupsList)
Ninad Palsule97e90da2023-05-17 14:04:52 -05001817{
Ninad Palsule3e72c202023-03-27 17:19:55 -05001818 std::vector<std::string> userGroups;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001819 std::vector<std::string> accountTypeUserGroups;
1820
1821 // If user specified account types then convert them to unix user groups
1822 if (accountTypes)
1823 {
1824 if (!getUserGroupFromAccountType(asyncResp->res, *accountTypes,
1825 accountTypeUserGroups))
1826 {
1827 // Problem in mapping Account Types to User Groups, Error already
1828 // logged.
1829 return;
1830 }
1831 }
1832
Ninad Palsule3e72c202023-03-27 17:19:55 -05001833 for (const auto& grp : allGroupsList)
1834 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001835 // If user specified the account type then only accept groups which are
1836 // in the account types group list.
1837 if (!accountTypeUserGroups.empty())
1838 {
1839 bool found = false;
1840 for (const auto& grp1 : accountTypeUserGroups)
1841 {
1842 if (grp == grp1)
1843 {
1844 found = true;
1845 break;
1846 }
1847 }
1848 if (!found)
1849 {
1850 continue;
1851 }
1852 }
1853
Ninad Palsule3e72c202023-03-27 17:19:55 -05001854 // Console access is provided to the user who is a member of
1855 // hostconsole group and has a administrator role. So, set
1856 // hostconsole group only for the administrator.
Ninad Palsule9ba73932023-06-01 16:38:57 -05001857 if ((grp == "hostconsole") && (roleId != "priv-admin"))
Ninad Palsule3e72c202023-03-27 17:19:55 -05001858 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001859 if (!accountTypeUserGroups.empty())
1860 {
Ed Tanous62598e32023-07-17 17:06:25 -07001861 BMCWEB_LOG_ERROR(
1862 "Only administrator can get HostConsole access");
Ninad Palsule9ba73932023-06-01 16:38:57 -05001863 asyncResp->res.result(boost::beast::http::status::bad_request);
1864 return;
1865 }
1866 continue;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001867 }
Ninad Palsule9ba73932023-06-01 16:38:57 -05001868 userGroups.emplace_back(grp);
1869 }
1870
1871 // Make sure user specified groups are valid. This is internal error because
1872 // it some inconsistencies between user manager and bmcweb.
1873 if (!accountTypeUserGroups.empty() &&
1874 accountTypeUserGroups.size() != userGroups.size())
1875 {
1876 messages::internalError(asyncResp->res);
1877 return;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001878 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001879 crow::connections::systemBus->async_method_call(
1880 [asyncResp, username, password](const boost::system::error_code& ec2,
1881 sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001882 processAfterCreateUser(asyncResp, username, password, ec2, m);
1883 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001884 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ninad Palsule3e72c202023-03-27 17:19:55 -05001885 "xyz.openbmc_project.User.Manager", "CreateUser", username, userGroups,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001886 roleId, enabled);
Ninad Palsule97e90da2023-05-17 14:04:52 -05001887}
1888
Ed Tanous1ef4c342022-05-12 16:12:36 -07001889inline void handleAccountCollectionPost(
1890 App& app, const crow::Request& req,
1891 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1892{
1893 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1894 {
1895 return;
1896 }
1897 std::string username;
1898 std::string password;
Ed Tanouse01d0c32023-06-30 13:21:32 -07001899 std::optional<std::string> roleIdJson;
1900 std::optional<bool> enabledJson;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001901 std::optional<std::vector<std::string>> accountTypes;
Myung Baeafc474a2024-10-09 00:53:29 -07001902 if (!json_util::readJsonPatch( //
1903 req, asyncResp->res, //
1904 "AccountTypes", accountTypes, //
1905 "Enabled", enabledJson, //
1906 "Password", password, //
1907 "RoleId", roleIdJson, //
1908 "UserName", username //
1909 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001910 {
1911 return;
1912 }
1913
Ed Tanouse01d0c32023-06-30 13:21:32 -07001914 std::string roleId = roleIdJson.value_or("User");
1915 std::string priv = getPrivilegeFromRoleId(roleId);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001916 if (priv.empty())
1917 {
Ed Tanouse01d0c32023-06-30 13:21:32 -07001918 messages::propertyValueNotInList(asyncResp->res, roleId, "RoleId");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001919 return;
1920 }
Asmitha Karunanithi239adf82022-03-25 02:59:03 -05001921 roleId = priv;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001922
Ed Tanouse01d0c32023-06-30 13:21:32 -07001923 bool enabled = enabledJson.value_or(true);
1924
Ed Tanous1ef4c342022-05-12 16:12:36 -07001925 // Reading AllGroups property
1926 sdbusplus::asio::getProperty<std::vector<std::string>>(
1927 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1928 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager",
1929 "AllGroups",
Ninad Palsule9ba73932023-06-01 16:38:57 -05001930 [asyncResp, username, password{std::move(password)}, roleId, enabled,
1931 accountTypes](const boost::system::error_code& ec,
1932 const std::vector<std::string>& allGroupsList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001933 if (ec)
1934 {
Gunnar Millsa0735a42024-09-06 12:51:11 -05001935 BMCWEB_LOG_ERROR("D-Bus response error {}", ec);
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001936 messages::internalError(asyncResp->res);
1937 return;
1938 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001939
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001940 if (allGroupsList.empty())
1941 {
1942 messages::internalError(asyncResp->res);
1943 return;
1944 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001945
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001946 processAfterGetAllGroups(asyncResp, username, password, roleId,
1947 enabled, accountTypes, allGroupsList);
1948 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001949}
1950
1951inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001952 handleAccountHead(App& app, const crow::Request& req,
1953 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1954 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001955{
1956 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1957 {
1958 return;
1959 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001960 asyncResp->res.addHeader(
1961 boost::beast::http::field::link,
1962 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1963}
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001964
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001965inline void
1966 handleAccountGet(App& app, const crow::Request& req,
1967 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1968 const std::string& accountName)
1969{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001970 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1971 {
1972 return;
1973 }
1974 asyncResp->res.addHeader(
1975 boost::beast::http::field::link,
1976 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1977
Ed Tanous25b54db2024-04-17 15:40:31 -07001978 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
1979 {
1980 // If authentication is disabled, there are no user accounts
1981 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1982 accountName);
1983 return;
1984 }
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001985
Ed Tanous1ef4c342022-05-12 16:12:36 -07001986 if (req.session == nullptr)
1987 {
1988 messages::internalError(asyncResp->res);
1989 return;
1990 }
1991 if (req.session->username != accountName)
1992 {
1993 // At this point we've determined that the user is trying to
1994 // modify a user that isn't them. We need to verify that they
1995 // have permissions to modify other users, so re-run the auth
1996 // check with the same permissions, minus ConfigureSelf.
1997 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001998 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001999 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
2000 "ConfigureManager"};
2001 if (!effectiveUserPrivileges.isSupersetOf(
2002 requiredPermissionsToChangeNonSelf))
2003 {
Ed Tanous62598e32023-07-17 17:06:25 -07002004 BMCWEB_LOG_DEBUG("GET Account denied access");
Ed Tanous1ef4c342022-05-12 16:12:36 -07002005 messages::insufficientPrivilege(asyncResp->res);
2006 return;
2007 }
2008 }
2009
George Liu5eb468d2023-06-20 17:03:24 +08002010 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
2011 dbus::utility::getManagedObjects(
2012 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07002013 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002014 accountName](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07002015 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002016 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002017 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002018 messages::internalError(asyncResp->res);
2019 return;
2020 }
2021 const auto userIt = std::ranges::find_if(
2022 users,
2023 [accountName](
2024 const std::pair<sdbusplus::message::object_path,
2025 dbus::utility::DBusInterfacesMap>& user) {
2026 return accountName == user.first.filename();
2027 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002028
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002029 if (userIt == users.end())
2030 {
2031 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2032 accountName);
2033 return;
2034 }
2035
2036 asyncResp->res.jsonValue["@odata.type"] =
2037 "#ManagerAccount.v1_7_0.ManagerAccount";
2038 asyncResp->res.jsonValue["Name"] = "User Account";
2039 asyncResp->res.jsonValue["Description"] = "User Account";
2040 asyncResp->res.jsonValue["Password"] = nullptr;
2041 asyncResp->res.jsonValue["StrictAccountTypes"] = true;
2042
2043 for (const auto& interface : userIt->second)
2044 {
2045 if (interface.first == "xyz.openbmc_project.User.Attributes")
2046 {
2047 for (const auto& property : interface.second)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002048 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002049 if (property.first == "UserEnabled")
Ed Tanous1ef4c342022-05-12 16:12:36 -07002050 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002051 const bool* userEnabled =
2052 std::get_if<bool>(&property.second);
2053 if (userEnabled == nullptr)
2054 {
2055 BMCWEB_LOG_ERROR("UserEnabled wasn't a bool");
2056 messages::internalError(asyncResp->res);
2057 return;
2058 }
2059 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
Ed Tanous1ef4c342022-05-12 16:12:36 -07002060 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002061 else if (property.first == "UserLockedForFailedAttempt")
Abhishek Patelc7229812022-02-01 10:07:15 -06002062 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002063 const bool* userLocked =
2064 std::get_if<bool>(&property.second);
2065 if (userLocked == nullptr)
2066 {
2067 BMCWEB_LOG_ERROR("UserLockedForF"
2068 "ailedAttempt "
2069 "wasn't a bool");
2070 messages::internalError(asyncResp->res);
2071 return;
2072 }
2073 asyncResp->res.jsonValue["Locked"] = *userLocked;
2074 nlohmann::json::array_t allowed;
2075 // can only unlock accounts
2076 allowed.emplace_back("false");
2077 asyncResp->res
2078 .jsonValue["Locked@Redfish.AllowableValues"] =
2079 std::move(allowed);
Abhishek Patelc7229812022-02-01 10:07:15 -06002080 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002081 else if (property.first == "UserPrivilege")
Abhishek Patelc7229812022-02-01 10:07:15 -06002082 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002083 const std::string* userPrivPtr =
2084 std::get_if<std::string>(&property.second);
2085 if (userPrivPtr == nullptr)
2086 {
2087 BMCWEB_LOG_ERROR("UserPrivilege wasn't a "
2088 "string");
2089 messages::internalError(asyncResp->res);
2090 return;
2091 }
2092 std::string role =
2093 getRoleIdFromPrivilege(*userPrivPtr);
2094 if (role.empty())
2095 {
2096 BMCWEB_LOG_ERROR("Invalid user role");
2097 messages::internalError(asyncResp->res);
2098 return;
2099 }
2100 asyncResp->res.jsonValue["RoleId"] = role;
2101
2102 nlohmann::json& roleEntry =
2103 asyncResp->res.jsonValue["Links"]["Role"];
2104 roleEntry["@odata.id"] = boost::urls::format(
2105 "/redfish/v1/AccountService/Roles/{}", role);
2106 }
2107 else if (property.first == "UserPasswordExpired")
2108 {
2109 const bool* userPasswordExpired =
2110 std::get_if<bool>(&property.second);
2111 if (userPasswordExpired == nullptr)
2112 {
2113 BMCWEB_LOG_ERROR(
2114 "UserPasswordExpired wasn't a bool");
2115 messages::internalError(asyncResp->res);
2116 return;
2117 }
2118 asyncResp->res.jsonValue["PasswordChangeRequired"] =
2119 *userPasswordExpired;
2120 }
2121 else if (property.first == "UserGroups")
2122 {
2123 const std::vector<std::string>* userGroups =
2124 std::get_if<std::vector<std::string>>(
2125 &property.second);
2126 if (userGroups == nullptr)
2127 {
2128 BMCWEB_LOG_ERROR(
2129 "userGroups wasn't a string vector");
2130 messages::internalError(asyncResp->res);
2131 return;
2132 }
2133 if (!translateUserGroup(*userGroups,
2134 asyncResp->res))
2135 {
2136 BMCWEB_LOG_ERROR("userGroups mapping failed");
2137 messages::internalError(asyncResp->res);
2138 return;
2139 }
Abhishek Patelc7229812022-02-01 10:07:15 -06002140 }
2141 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002142 }
2143 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002144
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002145 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
2146 "/redfish/v1/AccountService/Accounts/{}", accountName);
2147 asyncResp->res.jsonValue["Id"] = accountName;
2148 asyncResp->res.jsonValue["UserName"] = accountName;
2149 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002150}
2151
2152inline void
Gunnar Mills20fc3072023-01-27 15:13:36 -06002153 handleAccountDelete(App& app, const crow::Request& req,
2154 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2155 const std::string& username)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002156{
2157 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2158 {
2159 return;
2160 }
2161
Ed Tanous25b54db2024-04-17 15:40:31 -07002162 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2163 {
2164 // If authentication is disabled, there are no user accounts
2165 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2166 return;
2167 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002168 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
2169 tempObjPath /= username;
2170 const std::string userPath(tempObjPath);
2171
2172 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002173 [asyncResp, username](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002174 if (ec)
2175 {
2176 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2177 username);
2178 return;
2179 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002180
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002181 messages::accountRemoved(asyncResp->res);
2182 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002183 "xyz.openbmc_project.User.Manager", userPath,
2184 "xyz.openbmc_project.Object.Delete", "Delete");
2185}
2186
2187inline void
2188 handleAccountPatch(App& app, const crow::Request& req,
2189 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2190 const std::string& username)
2191{
2192 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2193 {
2194 return;
2195 }
Ed Tanous25b54db2024-04-17 15:40:31 -07002196 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2197 {
2198 // If authentication is disabled, there are no user accounts
2199 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2200 return;
2201 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002202 std::optional<std::string> newUserName;
2203 std::optional<std::string> password;
2204 std::optional<bool> enabled;
2205 std::optional<std::string> roleId;
2206 std::optional<bool> locked;
Abhishek Patel58345852022-02-02 08:54:25 -06002207 std::optional<std::vector<std::string>> accountTypes;
2208
Ed Tanous1ef4c342022-05-12 16:12:36 -07002209 if (req.session == nullptr)
2210 {
2211 messages::internalError(asyncResp->res);
2212 return;
2213 }
2214
Ed Tanous2b9c1df2024-04-06 13:52:01 -07002215 bool userSelf = (username == req.session->username);
2216
Ed Tanous1ef4c342022-05-12 16:12:36 -07002217 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002218 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002219 Privileges configureUsers = {"ConfigureUsers"};
2220 bool userHasConfigureUsers =
2221 effectiveUserPrivileges.isSupersetOf(configureUsers);
2222 if (userHasConfigureUsers)
2223 {
2224 // Users with ConfigureUsers can modify for all users
Myung Baeafc474a2024-10-09 00:53:29 -07002225 if (!json_util::readJsonPatch( //
2226 req, asyncResp->res, //
2227 "AccountTypes", accountTypes, //
2228 "Enabled", enabled, //
2229 "Locked", locked, //
2230 "Password", password, //
2231 "RoleId", roleId, //
2232 "UserName", newUserName //
2233 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07002234 {
2235 return;
2236 }
2237 }
2238 else
2239 {
2240 // ConfigureSelf accounts can only modify their own account
Abhishek Patel58345852022-02-02 08:54:25 -06002241 if (!userSelf)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002242 {
2243 messages::insufficientPrivilege(asyncResp->res);
2244 return;
2245 }
2246
2247 // ConfigureSelf accounts can only modify their password
2248 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
2249 password))
2250 {
2251 return;
2252 }
2253 }
2254
2255 // if user name is not provided in the patch method or if it
2256 // matches the user name in the URI, then we are treating it as
2257 // updating user properties other then username. If username
2258 // provided doesn't match the URI, then we are treating this as
2259 // user rename request.
2260 if (!newUserName || (newUserName.value() == username))
2261 {
2262 updateUserProperties(asyncResp, username, password, enabled, roleId,
Ravi Tejae518ef32024-05-16 10:33:08 -05002263 locked, accountTypes, userSelf, req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002264 return;
2265 }
2266 crow::connections::systemBus->async_method_call(
2267 [asyncResp, username, password(std::move(password)),
2268 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Ravi Tejae518ef32024-05-16 10:33:08 -05002269 locked, userSelf, req, accountTypes(std::move(accountTypes))](
Ed Tanouse81de512023-06-27 17:07:00 -07002270 const boost::system::error_code& ec, sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002271 if (ec)
2272 {
2273 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
2274 username);
2275 return;
2276 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002277
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002278 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
2279 locked, accountTypes, userSelf, req.session);
2280 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002281 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
2282 "xyz.openbmc_project.User.Manager", "RenameUser", username,
2283 *newUserName);
2284}
2285
Ed Tanous6c51eab2021-06-03 12:30:29 -07002286inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07002287{
Ed Tanous6c51eab2021-06-03 12:30:29 -07002288 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002289 .privileges(redfish::privileges::headAccountService)
2290 .methods(boost::beast::http::verb::head)(
2291 std::bind_front(handleAccountServiceHead, std::ref(app)));
2292
2293 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07002294 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002295 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002296 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002297
Ed Tanousf5ffd802021-07-19 10:55:33 -07002298 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06002299 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07002300 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002301 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07002302
Ed Tanous1aa375b2024-04-13 11:51:10 -07002303 BMCWEB_ROUTE(
2304 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002305 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002306 .privileges(redfish::privileges::headCertificateCollection)
2307 .methods(boost::beast::http::verb::head)(std::bind_front(
2308 handleAccountServiceClientCertificatesHead, std::ref(app)));
2309
2310 BMCWEB_ROUTE(
2311 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002312 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002313 .privileges(redfish::privileges::getCertificateCollection)
2314 .methods(boost::beast::http::verb::get)(std::bind_front(
2315 handleAccountServiceClientCertificatesGet, std::ref(app)));
2316
2317 BMCWEB_ROUTE(
2318 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002319 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002320 .privileges(redfish::privileges::headCertificate)
2321 .methods(boost::beast::http::verb::head)(std::bind_front(
2322 handleAccountServiceClientCertificatesInstanceHead, std::ref(app)));
2323
2324 BMCWEB_ROUTE(
2325 app,
2326 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
2327 .privileges(redfish::privileges::getCertificate)
2328 .methods(boost::beast::http::verb::get)(std::bind_front(
2329 handleAccountServiceClientCertificatesInstanceGet, std::ref(app)));
2330
Ed Tanous6c51eab2021-06-03 12:30:29 -07002331 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002332 .privileges(redfish::privileges::headManagerAccountCollection)
2333 .methods(boost::beast::http::verb::head)(
2334 std::bind_front(handleAccountCollectionHead, std::ref(app)));
2335
2336 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002337 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002338 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002339 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07002340
Ed Tanous6c51eab2021-06-03 12:30:29 -07002341 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002342 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002343 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002344 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07002345
2346 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002347 .privileges(redfish::privileges::headManagerAccount)
2348 .methods(boost::beast::http::verb::head)(
2349 std::bind_front(handleAccountHead, std::ref(app)));
2350
2351 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07002352 .privileges(redfish::privileges::getManagerAccount)
2353 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002354 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002355
2356 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002357 // TODO this privilege should be using the generated endpoints, but
2358 // because of the special handling of ConfigureSelf, it's not able to
2359 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07002360 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
2361 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002362 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002363
2364 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002365 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002366 .methods(boost::beast::http::verb::delete_)(
Gunnar Mills20fc3072023-01-27 15:13:36 -06002367 std::bind_front(handleAccountDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002368}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002369
Ed Tanous1abe55e2018-09-05 08:30:59 -07002370} // namespace redfish