blob: c772a45d8e3db9fbc3e39b851e746fa728c80bff [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
Ed Tanous492ec932024-12-09 15:42:19 -08001094struct UserUpdateParams
1095{
1096 std::string username;
1097 std::optional<std::string> password;
1098 std::optional<bool> enabled;
1099 std::optional<std::string> roleId;
1100 std::optional<bool> locked;
1101 std::optional<std::vector<std::string>> accountTypes;
1102 bool userSelf;
1103 std::shared_ptr<persistent_data::UserSession> session;
1104 std::string dbusObjectPath;
1105};
1106
1107inline void
1108 afterVerifyUserExists(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1109 const UserUpdateParams& params, int rc)
1110{
1111 if (rc <= 0)
1112 {
1113 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1114 params.username);
1115 return;
1116 }
1117
1118 if (params.password)
1119 {
1120 int retval = pamUpdatePassword(params.username, *params.password);
1121
1122 if (retval == PAM_USER_UNKNOWN)
1123 {
1124 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1125 params.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(params.username,
1145 params.session);
1146 messages::success(asyncResp->res);
1147 }
1148 }
1149
1150 if (params.enabled)
1151 {
1152 setDbusProperty(
1153 asyncResp, "Enabled", "xyz.openbmc_project.User.Manager",
1154 params.dbusObjectPath, "xyz.openbmc_project.User.Attributes",
1155 "UserEnabled", *params.enabled);
1156 }
1157
1158 if (params.roleId)
1159 {
1160 std::string priv = getPrivilegeFromRoleId(*params.roleId);
1161 if (priv.empty())
1162 {
1163 messages::propertyValueNotInList(asyncResp->res, true, "Locked");
1164 return;
1165 }
1166 setDbusProperty(asyncResp, "RoleId", "xyz.openbmc_project.User.Manager",
1167 params.dbusObjectPath,
1168 "xyz.openbmc_project.User.Attributes", "UserPrivilege",
1169 priv);
1170 }
1171
1172 if (params.locked)
1173 {
1174 // admin can unlock the account which is locked by
1175 // successive authentication failures but admin should
1176 // not be allowed to lock an account.
1177 if (*params.locked)
1178 {
1179 messages::propertyValueNotInList(asyncResp->res, "true", "Locked");
1180 return;
1181 }
1182 setDbusProperty(asyncResp, "Locked", "xyz.openbmc_project.User.Manager",
1183 params.dbusObjectPath,
1184 "xyz.openbmc_project.User.Attributes",
1185 "UserLockedForFailedAttempt", *params.locked);
1186 }
1187
1188 if (params.accountTypes)
1189 {
1190 patchAccountTypes(*params.accountTypes, asyncResp,
1191 params.dbusObjectPath, params.userSelf);
1192 }
1193}
1194
Abhishek Patel58345852022-02-02 08:54:25 -06001195inline void updateUserProperties(
Ed Tanous492ec932024-12-09 15:42:19 -08001196 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1197 const std::string& username, const std::optional<std::string>& password,
Abhishek Patel58345852022-02-02 08:54:25 -06001198 const std::optional<bool>& enabled,
1199 const std::optional<std::string>& roleId, const std::optional<bool>& locked,
Ed Tanous492ec932024-12-09 15:42:19 -08001200 const std::optional<std::vector<std::string>>& accountTypes, bool userSelf,
Ravi Tejae518ef32024-05-16 10:33:08 -05001201 const std::shared_ptr<persistent_data::UserSession>& session)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001202{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301203 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1204 tempObjPath /= username;
1205 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001206
Ed Tanous492ec932024-12-09 15:42:19 -08001207 UserUpdateParams params{username, password, enabled,
1208 roleId, locked, accountTypes,
1209 userSelf, session, dbusObjectPath};
1210
Ed Tanous6c51eab2021-06-03 12:30:29 -07001211 dbus::utility::checkDbusPathExists(
Ravi Tejae518ef32024-05-16 10:33:08 -05001212 dbusObjectPath,
Ed Tanous492ec932024-12-09 15:42:19 -08001213 std::bind_front(afterVerifyUserExists, asyncResp, std::move(params)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07001214}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001215
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001216inline void handleAccountServiceHead(
1217 App& app, const crow::Request& req,
1218 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001219{
1220 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1221 {
1222 return;
1223 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001224 asyncResp->res.addHeader(
1225 boost::beast::http::field::link,
1226 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1227}
1228
1229inline void
Ed Tanous1aa375b2024-04-13 11:51:10 -07001230 getClientCertificates(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1231 const nlohmann::json::json_pointer& keyLocation)
1232{
1233 boost::urls::url url(
1234 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates");
1235 std::array<std::string_view, 1> interfaces = {
1236 "xyz.openbmc_project.Certs.Certificate"};
1237 std::string path = "/xyz/openbmc_project/certs/authority/truststore";
1238
1239 collection_util::getCollectionToKey(asyncResp, url, interfaces, path,
1240 keyLocation);
1241}
1242
1243inline void handleAccountServiceClientCertificatesInstanceHead(
1244 App& app, const crow::Request& req,
1245 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1246 const std::string& /*id*/)
1247{
1248 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1249 {
1250 return;
1251 }
1252
1253 asyncResp->res.addHeader(
1254 boost::beast::http::field::link,
1255 "</redfish/v1/JsonSchemas/Certificate/Certificate.json>; rel=describedby");
1256}
1257
1258inline void handleAccountServiceClientCertificatesInstanceGet(
1259 App& app, const crow::Request& req,
1260 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1261{
1262 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1263 {
1264 return;
1265 }
1266 BMCWEB_LOG_DEBUG("ClientCertificate Certificate ID={}", id);
1267 const boost::urls::url certURL = boost::urls::format(
1268 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/{}",
1269 id);
1270 std::string objPath =
1271 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1272 getCertificateProperties(
1273 asyncResp, objPath,
1274 "xyz.openbmc_project.Certs.Manager.Authority.Truststore", id, certURL,
1275 "Client Certificate");
1276}
1277
1278inline void handleAccountServiceClientCertificatesHead(
1279 App& app, const crow::Request& req,
1280 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1281{
1282 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1283 {
1284 return;
1285 }
1286
1287 asyncResp->res.addHeader(
1288 boost::beast::http::field::link,
1289 "</redfish/v1/JsonSchemas/CertificateCollection/CertificateCollection.json>; rel=describedby");
1290}
1291
1292inline void handleAccountServiceClientCertificatesGet(
1293 App& app, const crow::Request& req,
1294 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1295{
1296 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1297 {
1298 return;
1299 }
1300 getClientCertificates(asyncResp, "/Members"_json_pointer);
1301}
1302
Ed Tanous3ce36882024-06-09 10:58:16 -07001303using account_service::CertificateMappingAttribute;
1304using persistent_data::MTLSCommonNameParseMode;
1305inline CertificateMappingAttribute
1306 getCertificateMapping(MTLSCommonNameParseMode parse)
1307{
1308 switch (parse)
1309 {
1310 case MTLSCommonNameParseMode::CommonName:
1311 {
1312 return CertificateMappingAttribute::CommonName;
1313 }
1314 break;
1315 case MTLSCommonNameParseMode::Whole:
1316 {
1317 return CertificateMappingAttribute::Whole;
1318 }
1319 break;
1320 case MTLSCommonNameParseMode::UserPrincipalName:
1321 {
1322 return CertificateMappingAttribute::UserPrincipalName;
1323 }
1324 break;
1325
1326 case MTLSCommonNameParseMode::Meta:
1327 {
1328 if constexpr (BMCWEB_META_TLS_COMMON_NAME_PARSING)
1329 {
1330 return CertificateMappingAttribute::CommonName;
1331 }
1332 }
1333 break;
1334 default:
1335 {
1336 return CertificateMappingAttribute::Invalid;
1337 }
1338 break;
1339 }
1340}
1341
Ed Tanous1aa375b2024-04-13 11:51:10 -07001342inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001343 handleAccountServiceGet(App& app, const crow::Request& req,
1344 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1345{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001346 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1347 {
1348 return;
1349 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001350
1351 if (req.session == nullptr)
1352 {
1353 messages::internalError(asyncResp->res);
1354 return;
1355 }
1356
Ed Tanousc1019822024-03-06 12:54:38 -08001357 const persistent_data::AuthConfigMethods& authMethodsConfig =
1358 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1359
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001360 asyncResp->res.addHeader(
1361 boost::beast::http::field::link,
1362 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1363
Ed Tanous1ef4c342022-05-12 16:12:36 -07001364 nlohmann::json& json = asyncResp->res.jsonValue;
1365 json["@odata.id"] = "/redfish/v1/AccountService";
Ravi Teja482a69e2024-04-22 06:56:13 -05001366 json["@odata.type"] = "#AccountService.v1_15_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001367 json["Id"] = "AccountService";
1368 json["Name"] = "Account Service";
1369 json["Description"] = "Account Service";
1370 json["ServiceEnabled"] = true;
1371 json["MaxPasswordLength"] = 20;
1372 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1373 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
Ravi Teja482a69e2024-04-22 06:56:13 -05001374 json["HTTPBasicAuth"] = authMethodsConfig.basic
1375 ? account_service::BasicAuthState::Enabled
1376 : account_service::BasicAuthState::Disabled;
1377
1378 nlohmann::json::array_t allowed;
1379 allowed.emplace_back(account_service::BasicAuthState::Enabled);
1380 allowed.emplace_back(account_service::BasicAuthState::Disabled);
1381 json["HTTPBasicAuth@AllowableValues"] = std::move(allowed);
1382
Ed Tanous1aa375b2024-04-13 11:51:10 -07001383 nlohmann::json::object_t clientCertificate;
1384 clientCertificate["Enabled"] = authMethodsConfig.tls;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001385 clientCertificate["RespondToUnauthenticatedClients"] =
1386 !authMethodsConfig.tlsStrict;
Ed Tanous3ce36882024-06-09 10:58:16 -07001387
1388 using account_service::CertificateMappingAttribute;
1389
1390 CertificateMappingAttribute mapping =
1391 getCertificateMapping(authMethodsConfig.mTLSCommonNameParsingMode);
1392 if (mapping == CertificateMappingAttribute::Invalid)
1393 {
1394 messages::internalError(asyncResp->res);
1395 }
1396 else
1397 {
1398 clientCertificate["CertificateMappingAttribute"] = mapping;
1399 }
Ed Tanous1aa375b2024-04-13 11:51:10 -07001400 nlohmann::json::object_t certificates;
1401 certificates["@odata.id"] =
1402 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates";
1403 certificates["@odata.type"] =
1404 "#CertificateCollection.CertificateCollection";
1405 clientCertificate["Certificates"] = std::move(certificates);
1406 json["MultiFactorAuth"]["ClientCertificate"] = std::move(clientCertificate);
1407
1408 getClientCertificates(
1409 asyncResp,
1410 "/MultiFactorAuth/ClientCertificate/Certificates/Members"_json_pointer);
1411
Ed Tanous1ef4c342022-05-12 16:12:36 -07001412 json["Oem"]["OpenBMC"]["@odata.type"] =
Ed Tanous5b5574a2022-09-26 19:53:36 -07001413 "#OpenBMCAccountService.v1_0_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001414 json["Oem"]["OpenBMC"]["@odata.id"] =
1415 "/redfish/v1/AccountService#/Oem/OpenBMC";
1416 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1417 authMethodsConfig.basic;
1418 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1419 authMethodsConfig.sessionToken;
1420 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1421 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1422 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1423
1424 // /redfish/v1/AccountService/LDAP/Certificates is something only
1425 // ConfigureManager can access then only display when the user has
1426 // permissions ConfigureManager
1427 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001428 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001429
1430 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1431 effectiveUserPrivileges))
1432 {
1433 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1434 "/redfish/v1/AccountService/LDAP/Certificates";
1435 }
Ed Tanousdeae6a72024-11-11 21:58:57 -08001436 dbus::utility::getAllProperties(
1437 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1438 "xyz.openbmc_project.User.AccountPolicy",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001439 [asyncResp](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001440 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001441 if (ec)
1442 {
1443 messages::internalError(asyncResp->res);
1444 return;
1445 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001446
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001447 BMCWEB_LOG_DEBUG("Got {} properties for AccountService",
1448 propertiesList.size());
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001449
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001450 const uint8_t* minPasswordLength = nullptr;
1451 const uint32_t* accountUnlockTimeout = nullptr;
1452 const uint16_t* maxLoginAttemptBeforeLockout = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001453
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001454 const bool success = sdbusplus::unpackPropertiesNoThrow(
1455 dbus_utils::UnpackErrorPrinter(), propertiesList,
1456 "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout",
1457 accountUnlockTimeout, "MaxLoginAttemptBeforeLockout",
1458 maxLoginAttemptBeforeLockout);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001459
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001460 if (!success)
1461 {
1462 messages::internalError(asyncResp->res);
1463 return;
1464 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001465
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001466 if (minPasswordLength != nullptr)
1467 {
1468 asyncResp->res.jsonValue["MinPasswordLength"] =
1469 *minPasswordLength;
1470 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001471
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001472 if (accountUnlockTimeout != nullptr)
1473 {
1474 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1475 *accountUnlockTimeout;
1476 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001477
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001478 if (maxLoginAttemptBeforeLockout != nullptr)
1479 {
1480 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1481 *maxLoginAttemptBeforeLockout;
1482 }
1483 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001484
Ed Tanous02cad962022-06-30 16:50:15 -07001485 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001486 const std::string& ldapType) {
1487 if (!success)
1488 {
1489 return;
1490 }
1491 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1492 };
1493
1494 getLDAPConfigData("LDAP", callback);
1495 getLDAPConfigData("ActiveDirectory", callback);
1496}
1497
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001498inline void handleCertificateMappingAttributePatch(
1499 crow::Response& res, const std::string& certMapAttribute)
Ed Tanous3ce36882024-06-09 10:58:16 -07001500{
1501 MTLSCommonNameParseMode parseMode =
1502 persistent_data::getMTLSCommonNameParseMode(certMapAttribute);
1503 if (parseMode == MTLSCommonNameParseMode::Invalid)
1504 {
1505 messages::propertyValueNotInList(res, "CertificateMappingAttribute",
1506 certMapAttribute);
1507 return;
1508 }
1509
1510 persistent_data::AuthConfigMethods& authMethodsConfig =
1511 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1512 authMethodsConfig.mTLSCommonNameParsingMode = parseMode;
1513}
1514
Ed Tanous3281bcf2024-06-25 16:02:05 -07001515inline void handleRespondToUnauthenticatedClientsPatch(
1516 App& app, const crow::Request& req, crow::Response& res,
1517 bool respondToUnauthenticatedClients)
1518{
1519 if (req.session != nullptr)
1520 {
1521 // Sanity check. If the user isn't currently authenticated with mutual
1522 // TLS, they very likely are about to permanently lock themselves out.
1523 // Make sure they're using mutual TLS before allowing locking.
1524 if (req.session->sessionType != persistent_data::SessionType::MutualTLS)
1525 {
1526 messages::propertyValueExternalConflict(
1527 res,
1528 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
1529 respondToUnauthenticatedClients);
1530 return;
1531 }
1532 }
1533
1534 persistent_data::AuthConfigMethods& authMethodsConfig =
1535 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1536
1537 // Change the settings
1538 authMethodsConfig.tlsStrict = !respondToUnauthenticatedClients;
1539
1540 // Write settings to disk
1541 persistent_data::getConfig().writeData();
1542
1543 // Trigger a reload, to apply the new settings to new connections
1544 app.loadCertificate();
1545}
1546
Ed Tanous1ef4c342022-05-12 16:12:36 -07001547inline void handleAccountServicePatch(
1548 App& app, const crow::Request& req,
1549 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1550{
1551 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1552 {
1553 return;
1554 }
1555 std::optional<uint32_t> unlockTimeout;
1556 std::optional<uint16_t> lockoutThreshold;
1557 std::optional<uint8_t> minPasswordLength;
1558 std::optional<uint16_t> maxPasswordLength;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001559 LdapPatchParams ldapObject;
Ed Tanous3ce36882024-06-09 10:58:16 -07001560 std::optional<std::string> certificateMappingAttribute;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001561 std::optional<bool> respondToUnauthenticatedClients;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001562 LdapPatchParams activeDirectoryObject;
Ed Tanousc1019822024-03-06 12:54:38 -08001563 AuthMethods auth;
Ravi Teja482a69e2024-04-22 06:56:13 -05001564 std::optional<std::string> httpBasicAuth;
Ed Tanous3ce36882024-06-09 10:58:16 -07001565
Myung Baeafc474a2024-10-09 00:53:29 -07001566 if (!json_util::readJsonPatch( //
1567 req, asyncResp->res, //
1568 "AccountLockoutDuration", unlockTimeout, //
1569 "AccountLockoutThreshold", lockoutThreshold, //
1570 "ActiveDirectory/Authentication/AuthenticationType",
1571 activeDirectoryObject.authType, //
1572 "ActiveDirectory/Authentication/Password",
1573 activeDirectoryObject.password, //
1574 "ActiveDirectory/Authentication/Username",
1575 activeDirectoryObject.userName, //
1576 "ActiveDirectory/LDAPService/SearchSettings/BaseDistinguishedNames",
1577 activeDirectoryObject.baseDNList, //
1578 "ActiveDirectory/LDAPService/SearchSettings/GroupsAttribute",
1579 activeDirectoryObject.groupsAttribute, //
1580 "ActiveDirectory/LDAPService/SearchSettings/UsernameAttribute",
1581 activeDirectoryObject.userNameAttribute, //
1582 "ActiveDirectory/RemoteRoleMapping",
1583 activeDirectoryObject.remoteRoleMapData, //
1584 "ActiveDirectory/ServiceAddresses",
1585 activeDirectoryObject.serviceAddressList, //
1586 "ActiveDirectory/ServiceEnabled",
1587 activeDirectoryObject.serviceEnabled, //
1588 "HTTPBasicAuth", httpBasicAuth, //
1589 "LDAP/Authentication/AuthenticationType", ldapObject.authType, //
1590 "LDAP/Authentication/Password", ldapObject.password, //
1591 "LDAP/Authentication/Username", ldapObject.userName, //
1592 "LDAP/LDAPService/SearchSettings/BaseDistinguishedNames",
1593 ldapObject.baseDNList, //
1594 "LDAP/LDAPService/SearchSettings/GroupsAttribute",
1595 ldapObject.groupsAttribute, //
1596 "LDAP/LDAPService/SearchSettings/UsernameAttribute",
1597 ldapObject.userNameAttribute, //
1598 "LDAP/RemoteRoleMapping", ldapObject.remoteRoleMapData, //
1599 "LDAP/ServiceAddresses", ldapObject.serviceAddressList, //
1600 "LDAP/ServiceEnabled", ldapObject.serviceEnabled, //
1601 "MaxPasswordLength", maxPasswordLength, //
1602 "MinPasswordLength", minPasswordLength, //
1603 "MultiFactorAuth/ClientCertificate/CertificateMappingAttribute",
1604 certificateMappingAttribute, //
1605 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
1606 respondToUnauthenticatedClients, //
1607 "Oem/OpenBMC/AuthMethods/BasicAuth", auth.basicAuth, //
1608 "Oem/OpenBMC/AuthMethods/Cookie", auth.cookie, //
1609 "Oem/OpenBMC/AuthMethods/SessionToken", auth.sessionToken, //
1610 "Oem/OpenBMC/AuthMethods/TLS", auth.tls, //
1611 "Oem/OpenBMC/AuthMethods/XToken", auth.xToken //
1612 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001613 {
1614 return;
1615 }
1616
Ravi Teja482a69e2024-04-22 06:56:13 -05001617 if (httpBasicAuth)
1618 {
1619 if (*httpBasicAuth == "Enabled")
1620 {
1621 auth.basicAuth = true;
1622 }
1623 else if (*httpBasicAuth == "Disabled")
1624 {
1625 auth.basicAuth = false;
1626 }
1627 else
1628 {
1629 messages::propertyValueNotInList(asyncResp->res, "HttpBasicAuth",
1630 *httpBasicAuth);
1631 }
1632 }
1633
Ed Tanous3281bcf2024-06-25 16:02:05 -07001634 if (respondToUnauthenticatedClients)
1635 {
1636 handleRespondToUnauthenticatedClientsPatch(
1637 app, req, asyncResp->res, *respondToUnauthenticatedClients);
1638 }
1639
Ed Tanous3ce36882024-06-09 10:58:16 -07001640 if (certificateMappingAttribute)
1641 {
1642 handleCertificateMappingAttributePatch(asyncResp->res,
1643 *certificateMappingAttribute);
1644 }
1645
Ed Tanous1ef4c342022-05-12 16:12:36 -07001646 if (minPasswordLength)
1647 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001648 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301649 asyncResp, "MinPasswordLength", "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001650 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001651 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
Ginu Georgee93abac2024-06-14 17:35:27 +05301652 *minPasswordLength);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001653 }
1654
1655 if (maxPasswordLength)
1656 {
1657 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1658 }
1659
Ed Tanous10cb44f2024-04-11 13:05:20 -07001660 handleLDAPPatch(std::move(activeDirectoryObject), asyncResp,
1661 "ActiveDirectory");
1662 handleLDAPPatch(std::move(ldapObject), asyncResp, "LDAP");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001663
Ed Tanousc1019822024-03-06 12:54:38 -08001664 handleAuthMethodsPatch(asyncResp, auth);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001665
Ed Tanous1ef4c342022-05-12 16:12:36 -07001666 if (unlockTimeout)
1667 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001668 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301669 asyncResp, "AccountLockoutDuration",
1670 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001671 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
Ed Tanous1ef4c342022-05-12 16:12:36 -07001672 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
Ginu Georgee93abac2024-06-14 17:35:27 +05301673 *unlockTimeout);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001674 }
1675 if (lockoutThreshold)
1676 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001677 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301678 asyncResp, "AccountLockoutThreshold",
1679 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001680 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001681 "xyz.openbmc_project.User.AccountPolicy",
Ginu Georgee93abac2024-06-14 17:35:27 +05301682 "MaxLoginAttemptBeforeLockout", *lockoutThreshold);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001683 }
1684}
1685
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001686inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001687 App& app, const crow::Request& req,
1688 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1689{
1690 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1691 {
1692 return;
1693 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001694 asyncResp->res.addHeader(
1695 boost::beast::http::field::link,
1696 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1697}
1698
1699inline void handleAccountCollectionGet(
1700 App& app, const crow::Request& req,
1701 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1702{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001703 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1704 {
1705 return;
1706 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001707
1708 if (req.session == nullptr)
1709 {
1710 messages::internalError(asyncResp->res);
1711 return;
1712 }
1713
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001714 asyncResp->res.addHeader(
1715 boost::beast::http::field::link,
1716 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001717
1718 asyncResp->res.jsonValue["@odata.id"] =
1719 "/redfish/v1/AccountService/Accounts";
1720 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1721 "ManagerAccountCollection";
1722 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1723 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1724
1725 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001726 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001727
1728 std::string thisUser;
1729 if (req.session)
1730 {
1731 thisUser = req.session->username;
1732 }
George Liu5eb468d2023-06-20 17:03:24 +08001733 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
1734 dbus::utility::getManagedObjects(
1735 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001736 [asyncResp, thisUser, effectiveUserPrivileges](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001737 const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001738 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001739 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001740 {
1741 messages::internalError(asyncResp->res);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001742 return;
1743 }
1744
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001745 bool userCanSeeAllAccounts =
1746 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1747
1748 bool userCanSeeSelf =
1749 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1750
1751 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1752 memberArray = nlohmann::json::array();
1753
1754 for (const auto& userpath : users)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001755 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001756 std::string user = userpath.first.filename();
1757 if (user.empty())
1758 {
1759 messages::internalError(asyncResp->res);
1760 BMCWEB_LOG_ERROR("Invalid firmware ID");
1761
1762 return;
1763 }
1764
1765 // As clarified by Redfish here:
1766 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1767 // Users without ConfigureUsers, only see their own
1768 // account. Users with ConfigureUsers, see all
1769 // accounts.
1770 if (userCanSeeAllAccounts ||
1771 (thisUser == user && userCanSeeSelf))
1772 {
1773 nlohmann::json::object_t member;
1774 member["@odata.id"] = boost::urls::format(
1775 "/redfish/v1/AccountService/Accounts/{}", user);
1776 memberArray.emplace_back(std::move(member));
1777 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001778 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001779 asyncResp->res.jsonValue["Members@odata.count"] =
1780 memberArray.size();
1781 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001782}
1783
Ninad Palsule97e90da2023-05-17 14:04:52 -05001784inline void processAfterCreateUser(
1785 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1786 const std::string& username, const std::string& password,
1787 const boost::system::error_code& ec, sdbusplus::message_t& m)
1788{
1789 if (ec)
1790 {
1791 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1792 return;
1793 }
1794
1795 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1796 {
1797 // At this point we have a user that's been
1798 // created, but the password set
1799 // failed.Something is wrong, so delete the user
1800 // that we've already created
1801 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1802 tempObjPath /= username;
1803 const std::string userPath(tempObjPath);
1804
1805 crow::connections::systemBus->async_method_call(
1806 [asyncResp, password](const boost::system::error_code& ec3) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001807 if (ec3)
1808 {
1809 messages::internalError(asyncResp->res);
1810 return;
1811 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001812
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001813 // If password is invalid
1814 messages::propertyValueFormatError(asyncResp->res, nullptr,
1815 "Password");
1816 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001817 "xyz.openbmc_project.User.Manager", userPath,
1818 "xyz.openbmc_project.Object.Delete", "Delete");
1819
Ed Tanous62598e32023-07-17 17:06:25 -07001820 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
Ninad Palsule97e90da2023-05-17 14:04:52 -05001821 return;
1822 }
1823
1824 messages::created(asyncResp->res);
1825 asyncResp->res.addHeader("Location",
1826 "/redfish/v1/AccountService/Accounts/" + username);
1827}
1828
1829inline void processAfterGetAllGroups(
1830 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1831 const std::string& username, const std::string& password,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001832 const std::string& roleId, bool enabled,
Ninad Palsule9ba73932023-06-01 16:38:57 -05001833 std::optional<std::vector<std::string>> accountTypes,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001834 const std::vector<std::string>& allGroupsList)
Ninad Palsule97e90da2023-05-17 14:04:52 -05001835{
Ninad Palsule3e72c202023-03-27 17:19:55 -05001836 std::vector<std::string> userGroups;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001837 std::vector<std::string> accountTypeUserGroups;
1838
1839 // If user specified account types then convert them to unix user groups
1840 if (accountTypes)
1841 {
1842 if (!getUserGroupFromAccountType(asyncResp->res, *accountTypes,
1843 accountTypeUserGroups))
1844 {
1845 // Problem in mapping Account Types to User Groups, Error already
1846 // logged.
1847 return;
1848 }
1849 }
1850
Ninad Palsule3e72c202023-03-27 17:19:55 -05001851 for (const auto& grp : allGroupsList)
1852 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001853 // If user specified the account type then only accept groups which are
1854 // in the account types group list.
1855 if (!accountTypeUserGroups.empty())
1856 {
1857 bool found = false;
1858 for (const auto& grp1 : accountTypeUserGroups)
1859 {
1860 if (grp == grp1)
1861 {
1862 found = true;
1863 break;
1864 }
1865 }
1866 if (!found)
1867 {
1868 continue;
1869 }
1870 }
1871
Ninad Palsule3e72c202023-03-27 17:19:55 -05001872 // Console access is provided to the user who is a member of
1873 // hostconsole group and has a administrator role. So, set
1874 // hostconsole group only for the administrator.
Ninad Palsule9ba73932023-06-01 16:38:57 -05001875 if ((grp == "hostconsole") && (roleId != "priv-admin"))
Ninad Palsule3e72c202023-03-27 17:19:55 -05001876 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001877 if (!accountTypeUserGroups.empty())
1878 {
Ed Tanous62598e32023-07-17 17:06:25 -07001879 BMCWEB_LOG_ERROR(
1880 "Only administrator can get HostConsole access");
Ninad Palsule9ba73932023-06-01 16:38:57 -05001881 asyncResp->res.result(boost::beast::http::status::bad_request);
1882 return;
1883 }
1884 continue;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001885 }
Ninad Palsule9ba73932023-06-01 16:38:57 -05001886 userGroups.emplace_back(grp);
1887 }
1888
1889 // Make sure user specified groups are valid. This is internal error because
1890 // it some inconsistencies between user manager and bmcweb.
1891 if (!accountTypeUserGroups.empty() &&
1892 accountTypeUserGroups.size() != userGroups.size())
1893 {
1894 messages::internalError(asyncResp->res);
1895 return;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001896 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001897 crow::connections::systemBus->async_method_call(
1898 [asyncResp, username, password](const boost::system::error_code& ec2,
1899 sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001900 processAfterCreateUser(asyncResp, username, password, ec2, m);
1901 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001902 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ninad Palsule3e72c202023-03-27 17:19:55 -05001903 "xyz.openbmc_project.User.Manager", "CreateUser", username, userGroups,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001904 roleId, enabled);
Ninad Palsule97e90da2023-05-17 14:04:52 -05001905}
1906
Ed Tanous1ef4c342022-05-12 16:12:36 -07001907inline void handleAccountCollectionPost(
1908 App& app, const crow::Request& req,
1909 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1910{
1911 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1912 {
1913 return;
1914 }
1915 std::string username;
1916 std::string password;
Ed Tanouse01d0c32023-06-30 13:21:32 -07001917 std::optional<std::string> roleIdJson;
1918 std::optional<bool> enabledJson;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001919 std::optional<std::vector<std::string>> accountTypes;
Myung Baeafc474a2024-10-09 00:53:29 -07001920 if (!json_util::readJsonPatch( //
1921 req, asyncResp->res, //
1922 "AccountTypes", accountTypes, //
1923 "Enabled", enabledJson, //
1924 "Password", password, //
1925 "RoleId", roleIdJson, //
1926 "UserName", username //
1927 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001928 {
1929 return;
1930 }
1931
Ed Tanouse01d0c32023-06-30 13:21:32 -07001932 std::string roleId = roleIdJson.value_or("User");
1933 std::string priv = getPrivilegeFromRoleId(roleId);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001934 if (priv.empty())
1935 {
Ed Tanouse01d0c32023-06-30 13:21:32 -07001936 messages::propertyValueNotInList(asyncResp->res, roleId, "RoleId");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001937 return;
1938 }
Asmitha Karunanithi239adf82022-03-25 02:59:03 -05001939 roleId = priv;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001940
Ed Tanouse01d0c32023-06-30 13:21:32 -07001941 bool enabled = enabledJson.value_or(true);
1942
Ed Tanous1ef4c342022-05-12 16:12:36 -07001943 // Reading AllGroups property
Ed Tanousdeae6a72024-11-11 21:58:57 -08001944 dbus::utility::getProperty<std::vector<std::string>>(
1945 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
1946 "xyz.openbmc_project.User.Manager", "AllGroups",
Ninad Palsule9ba73932023-06-01 16:38:57 -05001947 [asyncResp, username, password{std::move(password)}, roleId, enabled,
1948 accountTypes](const boost::system::error_code& ec,
1949 const std::vector<std::string>& allGroupsList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001950 if (ec)
1951 {
Gunnar Millsa0735a42024-09-06 12:51:11 -05001952 BMCWEB_LOG_ERROR("D-Bus response error {}", ec);
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001953 messages::internalError(asyncResp->res);
1954 return;
1955 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001956
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001957 if (allGroupsList.empty())
1958 {
1959 messages::internalError(asyncResp->res);
1960 return;
1961 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001962
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001963 processAfterGetAllGroups(asyncResp, username, password, roleId,
1964 enabled, accountTypes, allGroupsList);
1965 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001966}
1967
1968inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001969 handleAccountHead(App& app, const crow::Request& req,
1970 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1971 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001972{
1973 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1974 {
1975 return;
1976 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001977 asyncResp->res.addHeader(
1978 boost::beast::http::field::link,
1979 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1980}
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001981
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001982inline void
1983 handleAccountGet(App& app, const crow::Request& req,
1984 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1985 const std::string& accountName)
1986{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001987 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1988 {
1989 return;
1990 }
1991 asyncResp->res.addHeader(
1992 boost::beast::http::field::link,
1993 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1994
Ed Tanous25b54db2024-04-17 15:40:31 -07001995 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
1996 {
1997 // If authentication is disabled, there are no user accounts
1998 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1999 accountName);
2000 return;
2001 }
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08002002
Ed Tanous1ef4c342022-05-12 16:12:36 -07002003 if (req.session == nullptr)
2004 {
2005 messages::internalError(asyncResp->res);
2006 return;
2007 }
2008 if (req.session->username != accountName)
2009 {
2010 // At this point we've determined that the user is trying to
2011 // modify a user that isn't them. We need to verify that they
2012 // have permissions to modify other users, so re-run the auth
2013 // check with the same permissions, minus ConfigureSelf.
2014 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002015 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002016 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
2017 "ConfigureManager"};
2018 if (!effectiveUserPrivileges.isSupersetOf(
2019 requiredPermissionsToChangeNonSelf))
2020 {
Ed Tanous62598e32023-07-17 17:06:25 -07002021 BMCWEB_LOG_DEBUG("GET Account denied access");
Ed Tanous1ef4c342022-05-12 16:12:36 -07002022 messages::insufficientPrivilege(asyncResp->res);
2023 return;
2024 }
2025 }
2026
George Liu5eb468d2023-06-20 17:03:24 +08002027 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
2028 dbus::utility::getManagedObjects(
2029 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07002030 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002031 accountName](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07002032 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002033 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002034 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002035 messages::internalError(asyncResp->res);
2036 return;
2037 }
2038 const auto userIt = std::ranges::find_if(
2039 users,
2040 [accountName](
2041 const std::pair<sdbusplus::message::object_path,
2042 dbus::utility::DBusInterfacesMap>& user) {
2043 return accountName == user.first.filename();
2044 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002045
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002046 if (userIt == users.end())
2047 {
2048 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2049 accountName);
2050 return;
2051 }
2052
2053 asyncResp->res.jsonValue["@odata.type"] =
2054 "#ManagerAccount.v1_7_0.ManagerAccount";
2055 asyncResp->res.jsonValue["Name"] = "User Account";
2056 asyncResp->res.jsonValue["Description"] = "User Account";
2057 asyncResp->res.jsonValue["Password"] = nullptr;
2058 asyncResp->res.jsonValue["StrictAccountTypes"] = true;
2059
2060 for (const auto& interface : userIt->second)
2061 {
2062 if (interface.first == "xyz.openbmc_project.User.Attributes")
2063 {
Asmitha Karunanithib437a532024-12-20 04:54:25 -06002064 const bool* userEnabled = nullptr;
2065 const bool* userLocked = nullptr;
2066 const std::string* userPrivPtr = nullptr;
2067 const bool* userPasswordExpired = nullptr;
2068 const std::vector<std::string>* userGroups = nullptr;
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002069
Asmitha Karunanithib437a532024-12-20 04:54:25 -06002070 const bool success = sdbusplus::unpackPropertiesNoThrow(
2071 dbus_utils::UnpackErrorPrinter(), interface.second,
2072 "UserEnabled", userEnabled,
2073 "UserLockedForFailedAttempt", userLocked,
2074 "UserPrivilege", userPrivPtr, "UserPasswordExpired",
2075 userPasswordExpired, "UserGroups", userGroups);
2076 if (!success)
2077 {
2078 messages::internalError(asyncResp->res);
2079 return;
2080 }
2081 if (userEnabled == nullptr)
2082 {
2083 BMCWEB_LOG_ERROR("UserEnabled wasn't a bool");
2084 messages::internalError(asyncResp->res);
2085 return;
2086 }
2087 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
2088
2089 if (userLocked == nullptr)
2090 {
2091 BMCWEB_LOG_ERROR("UserLockedForF"
2092 "ailedAttempt "
2093 "wasn't a bool");
2094 messages::internalError(asyncResp->res);
2095 return;
2096 }
2097 asyncResp->res.jsonValue["Locked"] = *userLocked;
2098 nlohmann::json::array_t allowed;
2099 // can only unlock accounts
2100 allowed.emplace_back("false");
2101 asyncResp->res.jsonValue["Locked@Redfish.AllowableValues"] =
2102 std::move(allowed);
2103
2104 if (userPrivPtr == nullptr)
2105 {
2106 BMCWEB_LOG_ERROR("UserPrivilege wasn't a "
2107 "string");
2108 messages::internalError(asyncResp->res);
2109 return;
2110 }
2111 std::string role = getRoleIdFromPrivilege(*userPrivPtr);
2112 if (role.empty())
2113 {
2114 BMCWEB_LOG_ERROR("Invalid user role");
2115 messages::internalError(asyncResp->res);
2116 return;
2117 }
2118 asyncResp->res.jsonValue["RoleId"] = role;
2119
2120 nlohmann::json& roleEntry =
2121 asyncResp->res.jsonValue["Links"]["Role"];
2122 roleEntry["@odata.id"] = boost::urls::format(
2123 "/redfish/v1/AccountService/Roles/{}", role);
2124
2125 if (userPasswordExpired == nullptr)
2126 {
2127 BMCWEB_LOG_ERROR("UserPasswordExpired wasn't a bool");
2128 messages::internalError(asyncResp->res);
2129 return;
2130 }
2131 asyncResp->res.jsonValue["PasswordChangeRequired"] =
2132 *userPasswordExpired;
2133
2134 if (userGroups == nullptr)
2135 {
2136 BMCWEB_LOG_ERROR("userGroups wasn't a string vector");
2137 messages::internalError(asyncResp->res);
2138 return;
2139 }
2140 if (!translateUserGroup(*userGroups, asyncResp->res))
2141 {
2142 BMCWEB_LOG_ERROR("userGroups mapping failed");
2143 messages::internalError(asyncResp->res);
2144 return;
Abhishek Patelc7229812022-02-01 10:07:15 -06002145 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002146 }
2147 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002148
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002149 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
2150 "/redfish/v1/AccountService/Accounts/{}", accountName);
2151 asyncResp->res.jsonValue["Id"] = accountName;
2152 asyncResp->res.jsonValue["UserName"] = accountName;
2153 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002154}
2155
2156inline void
Gunnar Mills20fc3072023-01-27 15:13:36 -06002157 handleAccountDelete(App& app, const crow::Request& req,
2158 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2159 const std::string& username)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002160{
2161 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2162 {
2163 return;
2164 }
2165
Ed Tanous25b54db2024-04-17 15:40:31 -07002166 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2167 {
2168 // If authentication is disabled, there are no user accounts
2169 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2170 return;
2171 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002172 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
2173 tempObjPath /= username;
2174 const std::string userPath(tempObjPath);
2175
2176 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002177 [asyncResp, username](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002178 if (ec)
2179 {
2180 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2181 username);
2182 return;
2183 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002184
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002185 messages::accountRemoved(asyncResp->res);
2186 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002187 "xyz.openbmc_project.User.Manager", userPath,
2188 "xyz.openbmc_project.Object.Delete", "Delete");
2189}
2190
2191inline void
2192 handleAccountPatch(App& app, const crow::Request& req,
2193 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2194 const std::string& username)
2195{
2196 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2197 {
2198 return;
2199 }
Ed Tanous25b54db2024-04-17 15:40:31 -07002200 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2201 {
2202 // If authentication is disabled, there are no user accounts
2203 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2204 return;
2205 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002206 std::optional<std::string> newUserName;
2207 std::optional<std::string> password;
2208 std::optional<bool> enabled;
2209 std::optional<std::string> roleId;
2210 std::optional<bool> locked;
Abhishek Patel58345852022-02-02 08:54:25 -06002211 std::optional<std::vector<std::string>> accountTypes;
2212
Ed Tanous1ef4c342022-05-12 16:12:36 -07002213 if (req.session == nullptr)
2214 {
2215 messages::internalError(asyncResp->res);
2216 return;
2217 }
2218
Ed Tanous2b9c1df2024-04-06 13:52:01 -07002219 bool userSelf = (username == req.session->username);
2220
Ed Tanous1ef4c342022-05-12 16:12:36 -07002221 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002222 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002223 Privileges configureUsers = {"ConfigureUsers"};
2224 bool userHasConfigureUsers =
2225 effectiveUserPrivileges.isSupersetOf(configureUsers);
2226 if (userHasConfigureUsers)
2227 {
2228 // Users with ConfigureUsers can modify for all users
Myung Baeafc474a2024-10-09 00:53:29 -07002229 if (!json_util::readJsonPatch( //
2230 req, asyncResp->res, //
2231 "AccountTypes", accountTypes, //
2232 "Enabled", enabled, //
2233 "Locked", locked, //
2234 "Password", password, //
2235 "RoleId", roleId, //
2236 "UserName", newUserName //
2237 ))
Ed Tanous1ef4c342022-05-12 16:12:36 -07002238 {
2239 return;
2240 }
2241 }
2242 else
2243 {
2244 // ConfigureSelf accounts can only modify their own account
Abhishek Patel58345852022-02-02 08:54:25 -06002245 if (!userSelf)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002246 {
2247 messages::insufficientPrivilege(asyncResp->res);
2248 return;
2249 }
2250
2251 // ConfigureSelf accounts can only modify their password
2252 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
2253 password))
2254 {
2255 return;
2256 }
2257 }
2258
2259 // if user name is not provided in the patch method or if it
2260 // matches the user name in the URI, then we are treating it as
2261 // updating user properties other then username. If username
2262 // provided doesn't match the URI, then we are treating this as
2263 // user rename request.
2264 if (!newUserName || (newUserName.value() == username))
2265 {
2266 updateUserProperties(asyncResp, username, password, enabled, roleId,
Ravi Tejae518ef32024-05-16 10:33:08 -05002267 locked, accountTypes, userSelf, req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002268 return;
2269 }
2270 crow::connections::systemBus->async_method_call(
2271 [asyncResp, username, password(std::move(password)),
2272 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Ravi Tejae518ef32024-05-16 10:33:08 -05002273 locked, userSelf, req, accountTypes(std::move(accountTypes))](
Ed Tanouse81de512023-06-27 17:07:00 -07002274 const boost::system::error_code& ec, sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002275 if (ec)
2276 {
2277 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
2278 username);
2279 return;
2280 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002281
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002282 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
2283 locked, accountTypes, userSelf, req.session);
2284 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002285 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
2286 "xyz.openbmc_project.User.Manager", "RenameUser", username,
2287 *newUserName);
2288}
2289
Ed Tanous6c51eab2021-06-03 12:30:29 -07002290inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07002291{
Ed Tanous6c51eab2021-06-03 12:30:29 -07002292 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002293 .privileges(redfish::privileges::headAccountService)
2294 .methods(boost::beast::http::verb::head)(
2295 std::bind_front(handleAccountServiceHead, std::ref(app)));
2296
2297 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07002298 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002299 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002300 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002301
Ed Tanousf5ffd802021-07-19 10:55:33 -07002302 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06002303 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07002304 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002305 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07002306
Ed Tanous1aa375b2024-04-13 11:51:10 -07002307 BMCWEB_ROUTE(
2308 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002309 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002310 .privileges(redfish::privileges::headCertificateCollection)
2311 .methods(boost::beast::http::verb::head)(std::bind_front(
2312 handleAccountServiceClientCertificatesHead, std::ref(app)));
2313
2314 BMCWEB_ROUTE(
2315 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002316 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002317 .privileges(redfish::privileges::getCertificateCollection)
2318 .methods(boost::beast::http::verb::get)(std::bind_front(
2319 handleAccountServiceClientCertificatesGet, std::ref(app)));
2320
2321 BMCWEB_ROUTE(
2322 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002323 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002324 .privileges(redfish::privileges::headCertificate)
2325 .methods(boost::beast::http::verb::head)(std::bind_front(
2326 handleAccountServiceClientCertificatesInstanceHead, std::ref(app)));
2327
2328 BMCWEB_ROUTE(
2329 app,
2330 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
2331 .privileges(redfish::privileges::getCertificate)
2332 .methods(boost::beast::http::verb::get)(std::bind_front(
2333 handleAccountServiceClientCertificatesInstanceGet, std::ref(app)));
2334
Ed Tanous6c51eab2021-06-03 12:30:29 -07002335 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002336 .privileges(redfish::privileges::headManagerAccountCollection)
2337 .methods(boost::beast::http::verb::head)(
2338 std::bind_front(handleAccountCollectionHead, std::ref(app)));
2339
2340 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002341 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002342 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002343 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07002344
Ed Tanous6c51eab2021-06-03 12:30:29 -07002345 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002346 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002347 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002348 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07002349
2350 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002351 .privileges(redfish::privileges::headManagerAccount)
2352 .methods(boost::beast::http::verb::head)(
2353 std::bind_front(handleAccountHead, std::ref(app)));
2354
2355 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07002356 .privileges(redfish::privileges::getManagerAccount)
2357 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002358 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002359
2360 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002361 // TODO this privilege should be using the generated endpoints, but
2362 // because of the special handling of ConfigureSelf, it's not able to
2363 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07002364 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
2365 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002366 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002367
2368 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002369 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002370 .methods(boost::beast::http::verb::delete_)(
Gunnar Mills20fc3072023-01-27 15:13:36 -06002371 std::bind_front(handleAccountDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002372}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002373
Ed Tanous1abe55e2018-09-05 08:30:59 -07002374} // namespace redfish