blob: 2ec3e321d9728602a626709ad5bd52d1eed83cd7 [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
Ed Tanousc1019822024-03-06 12:54:38 -0800420 if (!json_util::readJsonObject(*obj, asyncResp->res, "RemoteGroup",
421 remoteGroup, "LocalRole", localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530422 {
423 continue;
424 }
425
426 // Update existing RoleMapping Object
427 if (index < roleMapObjData.size())
428 {
Ed Tanous62598e32023-07-17 17:06:25 -0700429 BMCWEB_LOG_DEBUG("Update Role Map Object");
Ratan Gupta06785242019-07-26 22:30:16 +0530430 // If "RemoteGroup" info is provided
431 if (remoteGroup)
432 {
Ed Tanousd02aad32024-02-13 14:43:34 -0800433 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530434 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800435 std::format("RemoteRoleMapping/{}/RemoteGroup", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530436 ldapDbusService, roleMapObjData[index].first,
437 "xyz.openbmc_project.User.PrivilegeMapperEntry",
438 "GroupName", *remoteGroup);
Ratan Gupta06785242019-07-26 22:30:16 +0530439 }
440
441 // If "LocalRole" info is provided
442 if (localRole)
443 {
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500444 std::string priv = getPrivilegeFromRoleId(*localRole);
445 if (priv.empty())
446 {
447 messages::propertyValueNotInList(
448 asyncResp->res, *localRole,
449 std::format("RemoteRoleMapping/{}/LocalRole",
450 index));
451 return;
452 }
Ed Tanousd02aad32024-02-13 14:43:34 -0800453 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530454 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800455 std::format("RemoteRoleMapping/{}/LocalRole", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530456 ldapDbusService, roleMapObjData[index].first,
457 "xyz.openbmc_project.User.PrivilegeMapperEntry",
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500458 "Privilege", priv);
Ratan Gupta06785242019-07-26 22:30:16 +0530459 }
460 }
461 // Create a new RoleMapping Object.
462 else
463 {
Ed Tanous62598e32023-07-17 17:06:25 -0700464 BMCWEB_LOG_DEBUG(
465 "setRoleMappingProperties: Creating new Object");
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400466 std::string pathString =
467 "RemoteRoleMapping/" + std::to_string(index);
Ratan Gupta06785242019-07-26 22:30:16 +0530468
469 if (!localRole)
470 {
471 messages::propertyMissing(asyncResp->res,
472 pathString + "/LocalRole");
473 continue;
474 }
475 if (!remoteGroup)
476 {
477 messages::propertyMissing(asyncResp->res,
478 pathString + "/RemoteGroup");
479 continue;
480 }
481
482 std::string dbusObjectPath;
483 if (serverType == "ActiveDirectory")
484 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700485 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530486 }
487 else if (serverType == "LDAP")
488 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000489 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530490 }
491
Ed Tanous62598e32023-07-17 17:06:25 -0700492 BMCWEB_LOG_DEBUG("Remote Group={},LocalRole={}", *remoteGroup,
493 *localRole);
Ratan Gupta06785242019-07-26 22:30:16 +0530494
495 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700496 [asyncResp, serverType, localRole,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800497 remoteGroup](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400498 if (ec)
499 {
500 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
501 messages::internalError(asyncResp->res);
502 return;
503 }
504 nlohmann::json& remoteRoleJson =
505 asyncResp->res
506 .jsonValue[serverType]["RemoteRoleMapping"];
507 nlohmann::json::object_t roleMapEntry;
508 roleMapEntry["LocalRole"] = *localRole;
509 roleMapEntry["RemoteGroup"] = *remoteGroup;
510 remoteRoleJson.emplace_back(std::move(roleMapEntry));
511 },
Ratan Gupta06785242019-07-26 22:30:16 +0530512 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700513 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530514 getPrivilegeFromRoleId(std::move(*localRole)));
515 }
516 }
517 }
518}
519
520/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530521 * Function that retrieves all properties for LDAP config object
522 * into JSON
523 */
524template <typename CallbackFunc>
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400525inline void
526 getLDAPConfigData(const std::string& ldapType, CallbackFunc&& callback)
Ratan Gupta6973a582018-12-13 18:25:44 +0530527{
George Liu2b731192023-01-11 16:27:13 +0800528 constexpr std::array<std::string_view, 2> interfaces = {
529 ldapEnableInterface, ldapConfigInterface};
Ratan Gupta6973a582018-12-13 18:25:44 +0530530
George Liu2b731192023-01-11 16:27:13 +0800531 dbus::utility::getDbusObject(
532 ldapConfigObjectName, interfaces,
Ed Tanous8cb2c022024-03-27 16:31:46 -0700533 [callback = std::forward<CallbackFunc>(callback),
Ed Tanousc1019822024-03-06 12:54:38 -0800534 ldapType](const boost::system::error_code& ec,
535 const dbus::utility::MapperGetObject& resp) mutable {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400536 if (ec || resp.empty())
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600537 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400538 BMCWEB_LOG_WARNING(
539 "DBUS response error during getting of service name: {}",
540 ec);
541 LDAPConfigData empty{};
542 callback(false, empty, ldapType);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600543 return;
544 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400545 std::string service = resp.begin()->first;
546 sdbusplus::message::object_path path(ldapRootObject);
547 dbus::utility::getManagedObjects(
548 service, path,
549 [callback, ldapType](const boost::system::error_code& ec2,
550 const dbus::utility::ManagedObjectType&
551 ldapObjects) mutable {
552 LDAPConfigData confData{};
553 if (ec2)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600554 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400555 callback(false, confData, ldapType);
556 BMCWEB_LOG_WARNING("D-Bus responses error: {}", ec2);
557 return;
558 }
559
560 std::string ldapDbusType;
561 std::string searchString;
562
563 if (ldapType == "LDAP")
564 {
565 ldapDbusType =
566 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
567 searchString = "openldap";
568 }
569 else if (ldapType == "ActiveDirectory")
570 {
571 ldapDbusType =
572 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
573 searchString = "active_directory";
574 }
575 else
576 {
577 BMCWEB_LOG_ERROR(
578 "Can't get the DbusType for the given type={}",
579 ldapType);
580 callback(false, confData, ldapType);
581 return;
582 }
583
584 std::string ldapEnableInterfaceStr = ldapEnableInterface;
585 std::string ldapConfigInterfaceStr = ldapConfigInterface;
586
587 for (const auto& object : ldapObjects)
588 {
589 // let's find the object whose ldap type is equal to the
590 // given type
591 if (object.first.str.find(searchString) ==
592 std::string::npos)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600593 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400594 continue;
595 }
596
597 for (const auto& interface : object.second)
598 {
599 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600600 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400601 // rest of the properties are string.
602 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600603 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400604 if (property.first == "Enabled")
605 {
606 const bool* value =
607 std::get_if<bool>(&property.second);
608 if (value == nullptr)
609 {
610 continue;
611 }
612 confData.serviceEnabled = *value;
613 break;
614 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600615 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400616 }
617 else if (interface.first == ldapConfigInterfaceStr)
618 {
619 for (const auto& property : interface.second)
620 {
621 const std::string* strValue =
622 std::get_if<std::string>(
623 &property.second);
624 if (strValue == nullptr)
625 {
626 continue;
627 }
628 if (property.first == "LDAPServerURI")
629 {
630 confData.uri = *strValue;
631 }
632 else if (property.first == "LDAPBindDN")
633 {
634 confData.bindDN = *strValue;
635 }
636 else if (property.first == "LDAPBaseDN")
637 {
638 confData.baseDN = *strValue;
639 }
640 else if (property.first ==
641 "LDAPSearchScope")
642 {
643 confData.searchScope = *strValue;
644 }
645 else if (property.first ==
646 "GroupNameAttribute")
647 {
648 confData.groupAttribute = *strValue;
649 }
650 else if (property.first ==
651 "UserNameAttribute")
652 {
653 confData.userNameAttribute = *strValue;
654 }
655 else if (property.first == "LDAPType")
656 {
657 confData.serverType = *strValue;
658 }
659 }
660 }
661 else if (
662 interface.first ==
663 "xyz.openbmc_project.User.PrivilegeMapperEntry")
664 {
665 LDAPRoleMapData roleMapData{};
666 for (const auto& property : interface.second)
667 {
668 const std::string* strValue =
669 std::get_if<std::string>(
670 &property.second);
671
672 if (strValue == nullptr)
673 {
674 continue;
675 }
676
677 if (property.first == "GroupName")
678 {
679 roleMapData.groupName = *strValue;
680 }
681 else if (property.first == "Privilege")
682 {
683 roleMapData.privilege = *strValue;
684 }
685 }
686
687 confData.groupRoleList.emplace_back(
688 object.first.str, roleMapData);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600689 }
690 }
691 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400692 callback(true, confData, ldapType);
693 });
George Liu2b731192023-01-11 16:27:13 +0800694 });
Ratan Gupta6973a582018-12-13 18:25:44 +0530695}
696
Ed Tanous6c51eab2021-06-03 12:30:29 -0700697/**
Ed Tanous6c51eab2021-06-03 12:30:29 -0700698 * @brief updates the LDAP server address and updates the
699 json response with the new value.
700 * @param serviceAddressList address to be updated.
701 * @param asyncResp pointer to the JSON response
702 * @param ldapServerElementName Type of LDAP
703 server(openLDAP/ActiveDirectory)
704 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530705
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700706inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700707 const std::vector<std::string>& serviceAddressList,
708 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
709 const std::string& ldapServerElementName,
710 const std::string& ldapConfigObject)
711{
Ginu Georgee93abac2024-06-14 17:35:27 +0530712 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceAddress",
713 ldapDbusService, ldapConfigObject, ldapConfigInterface,
714 "LDAPServerURI", serviceAddressList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700715}
716/**
717 * @brief updates the LDAP Bind DN and updates the
718 json response with the new value.
719 * @param username name of the user which needs to be updated.
720 * @param asyncResp pointer to the JSON response
721 * @param ldapServerElementName Type of LDAP
722 server(openLDAP/ActiveDirectory)
723 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530724
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700725inline void
726 handleUserNamePatch(const std::string& username,
727 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
728 const std::string& ldapServerElementName,
729 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700730{
Ginu Georgee93abac2024-06-14 17:35:27 +0530731 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800732 ldapServerElementName + "/Authentication/Username",
Ginu Georgee93abac2024-06-14 17:35:27 +0530733 ldapDbusService, ldapConfigObject, ldapConfigInterface,
734 "LDAPBindDN", username);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700735}
736
737/**
738 * @brief updates the LDAP password
739 * @param password : ldap password which needs to be updated.
740 * @param asyncResp pointer to the JSON response
741 * @param ldapServerElementName Type of LDAP
742 * server(openLDAP/ActiveDirectory)
743 */
744
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700745inline void
746 handlePasswordPatch(const std::string& password,
747 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
748 const std::string& ldapServerElementName,
749 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700750{
Ginu Georgee93abac2024-06-14 17:35:27 +0530751 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800752 ldapServerElementName + "/Authentication/Password",
Ginu Georgee93abac2024-06-14 17:35:27 +0530753 ldapDbusService, ldapConfigObject, ldapConfigInterface,
754 "LDAPBindDNPassword", password);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700755}
756
757/**
758 * @brief updates the LDAP BaseDN and updates the
759 json response with the new value.
760 * @param baseDNList baseDN list which needs to be updated.
761 * @param asyncResp pointer to the JSON response
762 * @param ldapServerElementName Type of LDAP
763 server(openLDAP/ActiveDirectory)
764 */
765
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700766inline void
767 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
768 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
769 const std::string& ldapServerElementName,
770 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700771{
Ginu Georgee93abac2024-06-14 17:35:27 +0530772 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800773 ldapServerElementName +
774 "/LDAPService/SearchSettings/BaseDistinguishedNames",
Ginu Georgee93abac2024-06-14 17:35:27 +0530775 ldapDbusService, ldapConfigObject, ldapConfigInterface,
776 "LDAPBaseDN", baseDNList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700777}
778/**
779 * @brief updates the LDAP user name attribute and updates the
780 json response with the new value.
781 * @param userNameAttribute attribute to be updated.
782 * @param asyncResp pointer to the JSON response
783 * @param ldapServerElementName Type of LDAP
784 server(openLDAP/ActiveDirectory)
785 */
786
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400787inline void handleUserNameAttrPatch(
788 const std::string& userNameAttribute,
789 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
790 const std::string& ldapServerElementName,
791 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700792{
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400793 setDbusProperty(
794 asyncResp,
795 ldapServerElementName + "LDAPService/SearchSettings/UsernameAttribute",
796 ldapDbusService, ldapConfigObject, ldapConfigInterface,
797 "UserNameAttribute", userNameAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700798}
799/**
800 * @brief updates the LDAP group attribute and updates the
801 json response with the new value.
802 * @param groupsAttribute attribute to be updated.
803 * @param asyncResp pointer to the JSON response
804 * @param ldapServerElementName Type of LDAP
805 server(openLDAP/ActiveDirectory)
806 */
807
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700808inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700809 const std::string& groupsAttribute,
810 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
811 const std::string& ldapServerElementName,
812 const std::string& ldapConfigObject)
813{
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400814 setDbusProperty(
815 asyncResp,
816 ldapServerElementName + "/LDAPService/SearchSettings/GroupsAttribute",
817 ldapDbusService, ldapConfigObject, ldapConfigInterface,
818 "GroupNameAttribute", groupsAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700819}
820/**
821 * @brief updates the LDAP service enable and updates the
822 json response with the new value.
823 * @param input JSON data.
824 * @param asyncResp pointer to the JSON response
825 * @param ldapServerElementName Type of LDAP
826 server(openLDAP/ActiveDirectory)
827 */
828
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700829inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700830 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
831 const std::string& ldapServerElementName,
832 const std::string& ldapConfigObject)
833{
Ginu Georgee93abac2024-06-14 17:35:27 +0530834 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceEnabled",
835 ldapDbusService, ldapConfigObject, ldapEnableInterface,
836 "Enabled", serviceEnabled);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700837}
838
Ed Tanousc1019822024-03-06 12:54:38 -0800839struct AuthMethods
Ed Tanous6c51eab2021-06-03 12:30:29 -0700840{
841 std::optional<bool> basicAuth;
842 std::optional<bool> cookie;
843 std::optional<bool> sessionToken;
844 std::optional<bool> xToken;
845 std::optional<bool> tls;
Ed Tanousc1019822024-03-06 12:54:38 -0800846};
Ed Tanous6c51eab2021-06-03 12:30:29 -0700847
Ed Tanousc1019822024-03-06 12:54:38 -0800848inline void
849 handleAuthMethodsPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
850 const AuthMethods& auth)
851{
852 persistent_data::AuthConfigMethods& authMethodsConfig =
Ed Tanous6c51eab2021-06-03 12:30:29 -0700853 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
854
Ed Tanousc1019822024-03-06 12:54:38 -0800855 if (auth.basicAuth)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700856 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700857 if constexpr (!BMCWEB_BASIC_AUTH)
858 {
859 messages::actionNotSupported(
860 asyncResp->res,
861 "Setting BasicAuth when basic-auth feature is disabled");
862 return;
863 }
864
Ed Tanousc1019822024-03-06 12:54:38 -0800865 authMethodsConfig.basic = *auth.basicAuth;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700866 }
867
Ed Tanousc1019822024-03-06 12:54:38 -0800868 if (auth.cookie)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700869 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700870 if constexpr (!BMCWEB_COOKIE_AUTH)
871 {
872 messages::actionNotSupported(
873 asyncResp->res,
874 "Setting Cookie when cookie-auth feature is disabled");
875 return;
876 }
Ed Tanousc1019822024-03-06 12:54:38 -0800877 authMethodsConfig.cookie = *auth.cookie;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700878 }
879
Ed Tanousc1019822024-03-06 12:54:38 -0800880 if (auth.sessionToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700881 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700882 if constexpr (!BMCWEB_SESSION_AUTH)
883 {
884 messages::actionNotSupported(
885 asyncResp->res,
886 "Setting SessionToken when session-auth feature is disabled");
887 return;
888 }
Ed Tanousc1019822024-03-06 12:54:38 -0800889 authMethodsConfig.sessionToken = *auth.sessionToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700890 }
891
Ed Tanousc1019822024-03-06 12:54:38 -0800892 if (auth.xToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700893 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700894 if constexpr (!BMCWEB_XTOKEN_AUTH)
895 {
896 messages::actionNotSupported(
897 asyncResp->res,
898 "Setting XToken when xtoken-auth feature is disabled");
899 return;
900 }
Ed Tanousc1019822024-03-06 12:54:38 -0800901 authMethodsConfig.xtoken = *auth.xToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700902 }
903
Ed Tanousc1019822024-03-06 12:54:38 -0800904 if (auth.tls)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700905 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700906 if constexpr (!BMCWEB_MUTUAL_TLS_AUTH)
907 {
908 messages::actionNotSupported(
909 asyncResp->res,
910 "Setting TLS when mutual-tls-auth feature is disabled");
911 return;
912 }
Ed Tanousc1019822024-03-06 12:54:38 -0800913 authMethodsConfig.tls = *auth.tls;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700914 }
915
916 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
917 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
918 !authMethodsConfig.tls)
919 {
920 // Do not allow user to disable everything
921 messages::actionNotSupported(asyncResp->res,
922 "of disabling all available methods");
923 return;
924 }
925
926 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
927 authMethodsConfig);
928 // Save configuration immediately
929 persistent_data::getConfig().writeData();
930
931 messages::success(asyncResp->res);
932}
933
934/**
935 * @brief Get the required values from the given JSON, validates the
936 * value and create the LDAP config object.
937 * @param input JSON data
938 * @param asyncResp pointer to the JSON response
939 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
940 */
941
Ed Tanous10cb44f2024-04-11 13:05:20 -0700942struct LdapPatchParams
943{
944 std::optional<std::string> authType;
945 std::optional<std::vector<std::string>> serviceAddressList;
946 std::optional<bool> serviceEnabled;
947 std::optional<std::vector<std::string>> baseDNList;
948 std::optional<std::string> userNameAttribute;
949 std::optional<std::string> groupsAttribute;
950 std::optional<std::string> userName;
951 std::optional<std::string> password;
952 std::optional<
953 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>
954 remoteRoleMapData;
955};
956
957inline void handleLDAPPatch(LdapPatchParams&& input,
Ed Tanous6c51eab2021-06-03 12:30:29 -0700958 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
959 const std::string& serverType)
960{
961 std::string dbusObjectPath;
962 if (serverType == "ActiveDirectory")
963 {
964 dbusObjectPath = adConfigObject;
965 }
966 else if (serverType == "LDAP")
967 {
968 dbusObjectPath = ldapConfigObjectName;
969 }
970 else
971 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700972 BMCWEB_LOG_ERROR("serverType wasn't AD or LDAP but was {}????",
973 serverType);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700974 return;
975 }
976
Ed Tanous10cb44f2024-04-11 13:05:20 -0700977 if (input.authType && *input.authType != "UsernameAndPassword")
Ed Tanous6c51eab2021-06-03 12:30:29 -0700978 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700979 messages::propertyValueNotInList(asyncResp->res, *input.authType,
Ed Tanousc1019822024-03-06 12:54:38 -0800980 "AuthenticationType");
981 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700982 }
Ed Tanousc1019822024-03-06 12:54:38 -0800983
Ed Tanous10cb44f2024-04-11 13:05:20 -0700984 if (input.serviceAddressList)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700985 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700986 if (input.serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530987 {
Ed Tanouse2616cc2022-06-27 12:45:55 -0700988 messages::propertyValueNotInList(
Ed Tanous10cb44f2024-04-11 13:05:20 -0700989 asyncResp->res, *input.serviceAddressList, "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +0000990 return;
991 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700992 }
Ed Tanous10cb44f2024-04-11 13:05:20 -0700993 if (input.baseDNList)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700994 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700995 if (input.baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +0530996 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700997 messages::propertyValueNotInList(asyncResp->res, *input.baseDNList,
Ed Tanous6c51eab2021-06-03 12:30:29 -0700998 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +0530999 return;
1000 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001001 }
Ratan Gupta8a07d282019-03-16 08:33:47 +05301002
Ed Tanous6c51eab2021-06-03 12:30:29 -07001003 // nothing to update, then return
Ed Tanous10cb44f2024-04-11 13:05:20 -07001004 if (!input.userName && !input.password && !input.serviceAddressList &&
1005 !input.baseDNList && !input.userNameAttribute &&
1006 !input.groupsAttribute && !input.serviceEnabled &&
1007 !input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001008 {
1009 return;
1010 }
1011
1012 // Get the existing resource first then keep modifying
1013 // whenever any property gets updated.
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001014 getLDAPConfigData(serverType, [asyncResp, input = std::move(input),
1015 dbusObjectPath = std::move(dbusObjectPath)](
1016 bool success,
1017 const LDAPConfigData& confData,
1018 const std::string& serverT) mutable {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001019 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301020 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001021 messages::internalError(asyncResp->res);
1022 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301023 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001024 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1025 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301026 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001027 // Disable the service first and update the rest of
1028 // the properties.
1029 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301030 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001031
Ed Tanous10cb44f2024-04-11 13:05:20 -07001032 if (input.serviceAddressList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301033 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001034 handleServiceAddressPatch(*input.serviceAddressList, asyncResp,
1035 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301036 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001037 if (input.userName)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001038 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001039 handleUserNamePatch(*input.userName, asyncResp, serverT,
1040 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001041 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001042 if (input.password)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001043 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001044 handlePasswordPatch(*input.password, asyncResp, serverT,
1045 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001046 }
1047
Ed Tanous10cb44f2024-04-11 13:05:20 -07001048 if (input.baseDNList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301049 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001050 handleBaseDNPatch(*input.baseDNList, asyncResp, serverT,
1051 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001052 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001053 if (input.userNameAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001054 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001055 handleUserNameAttrPatch(*input.userNameAttribute, asyncResp,
1056 serverT, dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001057 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001058 if (input.groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001059 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001060 handleGroupNameAttrPatch(*input.groupsAttribute, asyncResp, serverT,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001061 dbusObjectPath);
1062 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001063 if (input.serviceEnabled)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001064 {
1065 // if user has given the value as true then enable
1066 // the service. if user has given false then no-op
1067 // as service is already stopped.
Ed Tanous10cb44f2024-04-11 13:05:20 -07001068 if (*input.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301069 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001070 handleServiceEnablePatch(*input.serviceEnabled, asyncResp,
1071 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301072 }
1073 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001074 else
1075 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001076 // if user has not given the service enabled value
1077 // then revert it to the same state as it was
1078 // before.
1079 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1080 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001081 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001082
Ed Tanous10cb44f2024-04-11 13:05:20 -07001083 if (input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001084 {
1085 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001086 *input.remoteRoleMapData);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001087 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001088 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001089}
1090
Abhishek Patel58345852022-02-02 08:54:25 -06001091inline void updateUserProperties(
1092 std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& username,
1093 const std::optional<std::string>& password,
1094 const std::optional<bool>& enabled,
1095 const std::optional<std::string>& roleId, const std::optional<bool>& locked,
Ravi Tejae518ef32024-05-16 10:33:08 -05001096 std::optional<std::vector<std::string>> accountTypes, bool userSelf,
1097 const std::shared_ptr<persistent_data::UserSession>& session)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001098{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301099 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1100 tempObjPath /= username;
1101 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001102
1103 dbus::utility::checkDbusPathExists(
Ravi Tejae518ef32024-05-16 10:33:08 -05001104 dbusObjectPath,
1105 [dbusObjectPath, username, password, roleId, enabled, locked,
1106 accountTypes(std::move(accountTypes)), userSelf, session,
1107 asyncResp{std::move(asyncResp)}](int rc) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001108 if (rc <= 0)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001109 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001110 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1111 username);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001112 return;
1113 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001114
1115 if (password)
Ed Tanous618c14b2022-06-30 17:44:25 -07001116 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001117 int retval = pamUpdatePassword(username, *password);
1118
1119 if (retval == PAM_USER_UNKNOWN)
1120 {
1121 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1122 username);
1123 }
1124 else if (retval == PAM_AUTHTOK_ERR)
1125 {
1126 // If password is invalid
1127 messages::propertyValueFormatError(asyncResp->res, nullptr,
1128 "Password");
1129 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
1130 }
1131 else if (retval != PAM_SUCCESS)
1132 {
1133 messages::internalError(asyncResp->res);
1134 return;
1135 }
1136 else
1137 {
1138 // Remove existing sessions of the user when password
1139 // changed
1140 persistent_data::SessionStore::getInstance()
1141 .removeSessionsByUsernameExceptSession(username,
1142 session);
1143 messages::success(asyncResp->res);
1144 }
Ed Tanous618c14b2022-06-30 17:44:25 -07001145 }
1146
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001147 if (enabled)
Patrick Williams5a39f772023-10-20 11:20:21 -05001148 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001149 setDbusProperty(
1150 asyncResp, "Enabled", "xyz.openbmc_project.User.Manager",
1151 dbusObjectPath, "xyz.openbmc_project.User.Attributes",
1152 "UserEnabled", *enabled);
Ed Tanous618c14b2022-06-30 17:44:25 -07001153 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001154
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001155 if (roleId)
Abhishek Patel58345852022-02-02 08:54:25 -06001156 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001157 std::string priv = getPrivilegeFromRoleId(*roleId);
1158 if (priv.empty())
1159 {
1160 messages::propertyValueNotInList(asyncResp->res, true,
1161 "Locked");
1162 return;
1163 }
1164 setDbusProperty(
1165 asyncResp, "RoleId", "xyz.openbmc_project.User.Manager",
1166 dbusObjectPath, "xyz.openbmc_project.User.Attributes",
1167 "UserPrivilege", priv);
Abhishek Patel58345852022-02-02 08:54:25 -06001168 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001169
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001170 if (locked)
1171 {
1172 // admin can unlock the account which is locked by
1173 // successive authentication failures but admin should
1174 // not be allowed to lock an account.
1175 if (*locked)
1176 {
1177 messages::propertyValueNotInList(asyncResp->res, "true",
1178 "Locked");
1179 return;
1180 }
1181 setDbusProperty(
1182 asyncResp, "Locked", "xyz.openbmc_project.User.Manager",
1183 dbusObjectPath, "xyz.openbmc_project.User.Attributes",
1184 "UserLockedForFailedAttempt", *locked);
1185 }
1186
1187 if (accountTypes)
1188 {
1189 patchAccountTypes(*accountTypes, asyncResp, dbusObjectPath,
1190 userSelf);
1191 }
1192 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001193}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001194
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001195inline void handleAccountServiceHead(
1196 App& app, const crow::Request& req,
1197 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001198{
1199 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1200 {
1201 return;
1202 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001203 asyncResp->res.addHeader(
1204 boost::beast::http::field::link,
1205 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1206}
1207
1208inline void
Ed Tanous1aa375b2024-04-13 11:51:10 -07001209 getClientCertificates(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1210 const nlohmann::json::json_pointer& keyLocation)
1211{
1212 boost::urls::url url(
1213 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates");
1214 std::array<std::string_view, 1> interfaces = {
1215 "xyz.openbmc_project.Certs.Certificate"};
1216 std::string path = "/xyz/openbmc_project/certs/authority/truststore";
1217
1218 collection_util::getCollectionToKey(asyncResp, url, interfaces, path,
1219 keyLocation);
1220}
1221
1222inline void handleAccountServiceClientCertificatesInstanceHead(
1223 App& app, const crow::Request& req,
1224 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1225 const std::string& /*id*/)
1226{
1227 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1228 {
1229 return;
1230 }
1231
1232 asyncResp->res.addHeader(
1233 boost::beast::http::field::link,
1234 "</redfish/v1/JsonSchemas/Certificate/Certificate.json>; rel=describedby");
1235}
1236
1237inline void handleAccountServiceClientCertificatesInstanceGet(
1238 App& app, const crow::Request& req,
1239 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1240{
1241 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1242 {
1243 return;
1244 }
1245 BMCWEB_LOG_DEBUG("ClientCertificate Certificate ID={}", id);
1246 const boost::urls::url certURL = boost::urls::format(
1247 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/{}",
1248 id);
1249 std::string objPath =
1250 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1251 getCertificateProperties(
1252 asyncResp, objPath,
1253 "xyz.openbmc_project.Certs.Manager.Authority.Truststore", id, certURL,
1254 "Client Certificate");
1255}
1256
1257inline void handleAccountServiceClientCertificatesHead(
1258 App& app, const crow::Request& req,
1259 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1260{
1261 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1262 {
1263 return;
1264 }
1265
1266 asyncResp->res.addHeader(
1267 boost::beast::http::field::link,
1268 "</redfish/v1/JsonSchemas/CertificateCollection/CertificateCollection.json>; rel=describedby");
1269}
1270
1271inline void handleAccountServiceClientCertificatesGet(
1272 App& app, const crow::Request& req,
1273 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1274{
1275 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1276 {
1277 return;
1278 }
1279 getClientCertificates(asyncResp, "/Members"_json_pointer);
1280}
1281
Ed Tanous3ce36882024-06-09 10:58:16 -07001282using account_service::CertificateMappingAttribute;
1283using persistent_data::MTLSCommonNameParseMode;
1284inline CertificateMappingAttribute
1285 getCertificateMapping(MTLSCommonNameParseMode parse)
1286{
1287 switch (parse)
1288 {
1289 case MTLSCommonNameParseMode::CommonName:
1290 {
1291 return CertificateMappingAttribute::CommonName;
1292 }
1293 break;
1294 case MTLSCommonNameParseMode::Whole:
1295 {
1296 return CertificateMappingAttribute::Whole;
1297 }
1298 break;
1299 case MTLSCommonNameParseMode::UserPrincipalName:
1300 {
1301 return CertificateMappingAttribute::UserPrincipalName;
1302 }
1303 break;
1304
1305 case MTLSCommonNameParseMode::Meta:
1306 {
1307 if constexpr (BMCWEB_META_TLS_COMMON_NAME_PARSING)
1308 {
1309 return CertificateMappingAttribute::CommonName;
1310 }
1311 }
1312 break;
1313 default:
1314 {
1315 return CertificateMappingAttribute::Invalid;
1316 }
1317 break;
1318 }
1319}
1320
Ed Tanous1aa375b2024-04-13 11:51:10 -07001321inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001322 handleAccountServiceGet(App& app, const crow::Request& req,
1323 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1324{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001325 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1326 {
1327 return;
1328 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001329
1330 if (req.session == nullptr)
1331 {
1332 messages::internalError(asyncResp->res);
1333 return;
1334 }
1335
Ed Tanousc1019822024-03-06 12:54:38 -08001336 const persistent_data::AuthConfigMethods& authMethodsConfig =
1337 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1338
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001339 asyncResp->res.addHeader(
1340 boost::beast::http::field::link,
1341 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1342
Ed Tanous1ef4c342022-05-12 16:12:36 -07001343 nlohmann::json& json = asyncResp->res.jsonValue;
1344 json["@odata.id"] = "/redfish/v1/AccountService";
Ravi Teja482a69e2024-04-22 06:56:13 -05001345 json["@odata.type"] = "#AccountService.v1_15_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001346 json["Id"] = "AccountService";
1347 json["Name"] = "Account Service";
1348 json["Description"] = "Account Service";
1349 json["ServiceEnabled"] = true;
1350 json["MaxPasswordLength"] = 20;
1351 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1352 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
Ravi Teja482a69e2024-04-22 06:56:13 -05001353 json["HTTPBasicAuth"] = authMethodsConfig.basic
1354 ? account_service::BasicAuthState::Enabled
1355 : account_service::BasicAuthState::Disabled;
1356
1357 nlohmann::json::array_t allowed;
1358 allowed.emplace_back(account_service::BasicAuthState::Enabled);
1359 allowed.emplace_back(account_service::BasicAuthState::Disabled);
1360 json["HTTPBasicAuth@AllowableValues"] = std::move(allowed);
1361
Ed Tanous1aa375b2024-04-13 11:51:10 -07001362 nlohmann::json::object_t clientCertificate;
1363 clientCertificate["Enabled"] = authMethodsConfig.tls;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001364 clientCertificate["RespondToUnauthenticatedClients"] =
1365 !authMethodsConfig.tlsStrict;
Ed Tanous3ce36882024-06-09 10:58:16 -07001366
1367 using account_service::CertificateMappingAttribute;
1368
1369 CertificateMappingAttribute mapping =
1370 getCertificateMapping(authMethodsConfig.mTLSCommonNameParsingMode);
1371 if (mapping == CertificateMappingAttribute::Invalid)
1372 {
1373 messages::internalError(asyncResp->res);
1374 }
1375 else
1376 {
1377 clientCertificate["CertificateMappingAttribute"] = mapping;
1378 }
Ed Tanous1aa375b2024-04-13 11:51:10 -07001379 nlohmann::json::object_t certificates;
1380 certificates["@odata.id"] =
1381 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates";
1382 certificates["@odata.type"] =
1383 "#CertificateCollection.CertificateCollection";
1384 clientCertificate["Certificates"] = std::move(certificates);
1385 json["MultiFactorAuth"]["ClientCertificate"] = std::move(clientCertificate);
1386
1387 getClientCertificates(
1388 asyncResp,
1389 "/MultiFactorAuth/ClientCertificate/Certificates/Members"_json_pointer);
1390
Ed Tanous1ef4c342022-05-12 16:12:36 -07001391 json["Oem"]["OpenBMC"]["@odata.type"] =
Ed Tanous5b5574a2022-09-26 19:53:36 -07001392 "#OpenBMCAccountService.v1_0_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001393 json["Oem"]["OpenBMC"]["@odata.id"] =
1394 "/redfish/v1/AccountService#/Oem/OpenBMC";
1395 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1396 authMethodsConfig.basic;
1397 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1398 authMethodsConfig.sessionToken;
1399 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1400 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1401 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1402
1403 // /redfish/v1/AccountService/LDAP/Certificates is something only
1404 // ConfigureManager can access then only display when the user has
1405 // permissions ConfigureManager
1406 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001407 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001408
1409 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1410 effectiveUserPrivileges))
1411 {
1412 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1413 "/redfish/v1/AccountService/LDAP/Certificates";
1414 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001415 sdbusplus::asio::getAllProperties(
1416 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1417 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.AccountPolicy",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001418 [asyncResp](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001419 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001420 if (ec)
1421 {
1422 messages::internalError(asyncResp->res);
1423 return;
1424 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001425
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001426 BMCWEB_LOG_DEBUG("Got {} properties for AccountService",
1427 propertiesList.size());
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001428
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001429 const uint8_t* minPasswordLength = nullptr;
1430 const uint32_t* accountUnlockTimeout = nullptr;
1431 const uint16_t* maxLoginAttemptBeforeLockout = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001432
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001433 const bool success = sdbusplus::unpackPropertiesNoThrow(
1434 dbus_utils::UnpackErrorPrinter(), propertiesList,
1435 "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout",
1436 accountUnlockTimeout, "MaxLoginAttemptBeforeLockout",
1437 maxLoginAttemptBeforeLockout);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001438
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001439 if (!success)
1440 {
1441 messages::internalError(asyncResp->res);
1442 return;
1443 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001444
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001445 if (minPasswordLength != nullptr)
1446 {
1447 asyncResp->res.jsonValue["MinPasswordLength"] =
1448 *minPasswordLength;
1449 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001450
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001451 if (accountUnlockTimeout != nullptr)
1452 {
1453 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1454 *accountUnlockTimeout;
1455 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001456
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001457 if (maxLoginAttemptBeforeLockout != nullptr)
1458 {
1459 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1460 *maxLoginAttemptBeforeLockout;
1461 }
1462 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001463
Ed Tanous02cad962022-06-30 16:50:15 -07001464 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001465 const std::string& ldapType) {
1466 if (!success)
1467 {
1468 return;
1469 }
1470 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1471 };
1472
1473 getLDAPConfigData("LDAP", callback);
1474 getLDAPConfigData("ActiveDirectory", callback);
1475}
1476
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001477inline void handleCertificateMappingAttributePatch(
1478 crow::Response& res, const std::string& certMapAttribute)
Ed Tanous3ce36882024-06-09 10:58:16 -07001479{
1480 MTLSCommonNameParseMode parseMode =
1481 persistent_data::getMTLSCommonNameParseMode(certMapAttribute);
1482 if (parseMode == MTLSCommonNameParseMode::Invalid)
1483 {
1484 messages::propertyValueNotInList(res, "CertificateMappingAttribute",
1485 certMapAttribute);
1486 return;
1487 }
1488
1489 persistent_data::AuthConfigMethods& authMethodsConfig =
1490 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1491 authMethodsConfig.mTLSCommonNameParsingMode = parseMode;
1492}
1493
Ed Tanous3281bcf2024-06-25 16:02:05 -07001494inline void handleRespondToUnauthenticatedClientsPatch(
1495 App& app, const crow::Request& req, crow::Response& res,
1496 bool respondToUnauthenticatedClients)
1497{
1498 if (req.session != nullptr)
1499 {
1500 // Sanity check. If the user isn't currently authenticated with mutual
1501 // TLS, they very likely are about to permanently lock themselves out.
1502 // Make sure they're using mutual TLS before allowing locking.
1503 if (req.session->sessionType != persistent_data::SessionType::MutualTLS)
1504 {
1505 messages::propertyValueExternalConflict(
1506 res,
1507 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
1508 respondToUnauthenticatedClients);
1509 return;
1510 }
1511 }
1512
1513 persistent_data::AuthConfigMethods& authMethodsConfig =
1514 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1515
1516 // Change the settings
1517 authMethodsConfig.tlsStrict = !respondToUnauthenticatedClients;
1518
1519 // Write settings to disk
1520 persistent_data::getConfig().writeData();
1521
1522 // Trigger a reload, to apply the new settings to new connections
1523 app.loadCertificate();
1524}
1525
Ed Tanous1ef4c342022-05-12 16:12:36 -07001526inline void handleAccountServicePatch(
1527 App& app, const crow::Request& req,
1528 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1529{
1530 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1531 {
1532 return;
1533 }
1534 std::optional<uint32_t> unlockTimeout;
1535 std::optional<uint16_t> lockoutThreshold;
1536 std::optional<uint8_t> minPasswordLength;
1537 std::optional<uint16_t> maxPasswordLength;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001538 LdapPatchParams ldapObject;
Ed Tanous3ce36882024-06-09 10:58:16 -07001539 std::optional<std::string> certificateMappingAttribute;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001540 std::optional<bool> respondToUnauthenticatedClients;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001541 LdapPatchParams activeDirectoryObject;
Ed Tanousc1019822024-03-06 12:54:38 -08001542 AuthMethods auth;
Ravi Teja482a69e2024-04-22 06:56:13 -05001543 std::optional<std::string> httpBasicAuth;
Ed Tanous3ce36882024-06-09 10:58:16 -07001544
Ed Tanousc1019822024-03-06 12:54:38 -08001545 // clang-format off
Ed Tanous1ef4c342022-05-12 16:12:36 -07001546 if (!json_util::readJsonPatch(
Ed Tanousc1019822024-03-06 12:54:38 -08001547 req, asyncResp->res,
1548 "AccountLockoutDuration", unlockTimeout,
1549 "AccountLockoutThreshold", lockoutThreshold,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001550 "ActiveDirectory/Authentication/AuthenticationType", activeDirectoryObject.authType,
1551 "ActiveDirectory/Authentication/Password", activeDirectoryObject.password,
1552 "ActiveDirectory/Authentication/Username", activeDirectoryObject.userName,
1553 "ActiveDirectory/LDAPService/SearchSettings/BaseDistinguishedNames", activeDirectoryObject.baseDNList,
1554 "ActiveDirectory/LDAPService/SearchSettings/GroupsAttribute", activeDirectoryObject.groupsAttribute,
1555 "ActiveDirectory/LDAPService/SearchSettings/UsernameAttribute", activeDirectoryObject.userNameAttribute,
1556 "ActiveDirectory/RemoteRoleMapping", activeDirectoryObject.remoteRoleMapData,
1557 "ActiveDirectory/ServiceAddresses", activeDirectoryObject.serviceAddressList,
1558 "ActiveDirectory/ServiceEnabled", activeDirectoryObject.serviceEnabled,
Ed Tanous3ce36882024-06-09 10:58:16 -07001559 "MultiFactorAuth/ClientCertificate/CertificateMappingAttribute", certificateMappingAttribute,
Ed Tanous3281bcf2024-06-25 16:02:05 -07001560 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients", respondToUnauthenticatedClients,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001561 "LDAP/Authentication/AuthenticationType", ldapObject.authType,
1562 "LDAP/Authentication/Password", ldapObject.password,
1563 "LDAP/Authentication/Username", ldapObject.userName,
1564 "LDAP/LDAPService/SearchSettings/BaseDistinguishedNames", ldapObject.baseDNList,
1565 "LDAP/LDAPService/SearchSettings/GroupsAttribute", ldapObject.groupsAttribute,
1566 "LDAP/LDAPService/SearchSettings/UsernameAttribute", ldapObject.userNameAttribute,
1567 "LDAP/RemoteRoleMapping", ldapObject.remoteRoleMapData,
1568 "LDAP/ServiceAddresses", ldapObject.serviceAddressList,
1569 "LDAP/ServiceEnabled", ldapObject.serviceEnabled,
Ed Tanousc1019822024-03-06 12:54:38 -08001570 "MaxPasswordLength", maxPasswordLength,
1571 "MinPasswordLength", minPasswordLength,
Ed Tanousc1019822024-03-06 12:54:38 -08001572 "Oem/OpenBMC/AuthMethods/BasicAuth", auth.basicAuth,
1573 "Oem/OpenBMC/AuthMethods/Cookie", auth.cookie,
1574 "Oem/OpenBMC/AuthMethods/SessionToken", auth.sessionToken,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001575 "Oem/OpenBMC/AuthMethods/TLS", auth.tls,
Ravi Teja482a69e2024-04-22 06:56:13 -05001576 "Oem/OpenBMC/AuthMethods/XToken", auth.xToken,
1577 "HTTPBasicAuth", httpBasicAuth))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001578 {
1579 return;
1580 }
Ed Tanousc1019822024-03-06 12:54:38 -08001581 // clang-format on
Ed Tanous1ef4c342022-05-12 16:12:36 -07001582
Ravi Teja482a69e2024-04-22 06:56:13 -05001583 if (httpBasicAuth)
1584 {
1585 if (*httpBasicAuth == "Enabled")
1586 {
1587 auth.basicAuth = true;
1588 }
1589 else if (*httpBasicAuth == "Disabled")
1590 {
1591 auth.basicAuth = false;
1592 }
1593 else
1594 {
1595 messages::propertyValueNotInList(asyncResp->res, "HttpBasicAuth",
1596 *httpBasicAuth);
1597 }
1598 }
1599
Ed Tanous3281bcf2024-06-25 16:02:05 -07001600 if (respondToUnauthenticatedClients)
1601 {
1602 handleRespondToUnauthenticatedClientsPatch(
1603 app, req, asyncResp->res, *respondToUnauthenticatedClients);
1604 }
1605
Ed Tanous3ce36882024-06-09 10:58:16 -07001606 if (certificateMappingAttribute)
1607 {
1608 handleCertificateMappingAttributePatch(asyncResp->res,
1609 *certificateMappingAttribute);
1610 }
1611
Ed Tanous1ef4c342022-05-12 16:12:36 -07001612 if (minPasswordLength)
1613 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001614 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301615 asyncResp, "MinPasswordLength", "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001616 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001617 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
Ginu Georgee93abac2024-06-14 17:35:27 +05301618 *minPasswordLength);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001619 }
1620
1621 if (maxPasswordLength)
1622 {
1623 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1624 }
1625
Ed Tanous10cb44f2024-04-11 13:05:20 -07001626 handleLDAPPatch(std::move(activeDirectoryObject), asyncResp,
1627 "ActiveDirectory");
1628 handleLDAPPatch(std::move(ldapObject), asyncResp, "LDAP");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001629
Ed Tanousc1019822024-03-06 12:54:38 -08001630 handleAuthMethodsPatch(asyncResp, auth);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001631
Ed Tanous1ef4c342022-05-12 16:12:36 -07001632 if (unlockTimeout)
1633 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001634 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301635 asyncResp, "AccountLockoutDuration",
1636 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001637 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
Ed Tanous1ef4c342022-05-12 16:12:36 -07001638 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
Ginu Georgee93abac2024-06-14 17:35:27 +05301639 *unlockTimeout);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001640 }
1641 if (lockoutThreshold)
1642 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001643 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301644 asyncResp, "AccountLockoutThreshold",
1645 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001646 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001647 "xyz.openbmc_project.User.AccountPolicy",
Ginu Georgee93abac2024-06-14 17:35:27 +05301648 "MaxLoginAttemptBeforeLockout", *lockoutThreshold);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001649 }
1650}
1651
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001652inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001653 App& app, const crow::Request& req,
1654 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1655{
1656 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1657 {
1658 return;
1659 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001660 asyncResp->res.addHeader(
1661 boost::beast::http::field::link,
1662 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1663}
1664
1665inline void handleAccountCollectionGet(
1666 App& app, const crow::Request& req,
1667 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1668{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001669 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1670 {
1671 return;
1672 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001673
1674 if (req.session == nullptr)
1675 {
1676 messages::internalError(asyncResp->res);
1677 return;
1678 }
1679
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001680 asyncResp->res.addHeader(
1681 boost::beast::http::field::link,
1682 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001683
1684 asyncResp->res.jsonValue["@odata.id"] =
1685 "/redfish/v1/AccountService/Accounts";
1686 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1687 "ManagerAccountCollection";
1688 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1689 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1690
1691 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001692 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001693
1694 std::string thisUser;
1695 if (req.session)
1696 {
1697 thisUser = req.session->username;
1698 }
George Liu5eb468d2023-06-20 17:03:24 +08001699 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
1700 dbus::utility::getManagedObjects(
1701 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001702 [asyncResp, thisUser, effectiveUserPrivileges](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001703 const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001704 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001705 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001706 {
1707 messages::internalError(asyncResp->res);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001708 return;
1709 }
1710
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001711 bool userCanSeeAllAccounts =
1712 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1713
1714 bool userCanSeeSelf =
1715 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1716
1717 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1718 memberArray = nlohmann::json::array();
1719
1720 for (const auto& userpath : users)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001721 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001722 std::string user = userpath.first.filename();
1723 if (user.empty())
1724 {
1725 messages::internalError(asyncResp->res);
1726 BMCWEB_LOG_ERROR("Invalid firmware ID");
1727
1728 return;
1729 }
1730
1731 // As clarified by Redfish here:
1732 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1733 // Users without ConfigureUsers, only see their own
1734 // account. Users with ConfigureUsers, see all
1735 // accounts.
1736 if (userCanSeeAllAccounts ||
1737 (thisUser == user && userCanSeeSelf))
1738 {
1739 nlohmann::json::object_t member;
1740 member["@odata.id"] = boost::urls::format(
1741 "/redfish/v1/AccountService/Accounts/{}", user);
1742 memberArray.emplace_back(std::move(member));
1743 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001744 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001745 asyncResp->res.jsonValue["Members@odata.count"] =
1746 memberArray.size();
1747 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001748}
1749
Ninad Palsule97e90da2023-05-17 14:04:52 -05001750inline void processAfterCreateUser(
1751 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1752 const std::string& username, const std::string& password,
1753 const boost::system::error_code& ec, sdbusplus::message_t& m)
1754{
1755 if (ec)
1756 {
1757 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1758 return;
1759 }
1760
1761 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1762 {
1763 // At this point we have a user that's been
1764 // created, but the password set
1765 // failed.Something is wrong, so delete the user
1766 // that we've already created
1767 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1768 tempObjPath /= username;
1769 const std::string userPath(tempObjPath);
1770
1771 crow::connections::systemBus->async_method_call(
1772 [asyncResp, password](const boost::system::error_code& ec3) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001773 if (ec3)
1774 {
1775 messages::internalError(asyncResp->res);
1776 return;
1777 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001778
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001779 // If password is invalid
1780 messages::propertyValueFormatError(asyncResp->res, nullptr,
1781 "Password");
1782 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001783 "xyz.openbmc_project.User.Manager", userPath,
1784 "xyz.openbmc_project.Object.Delete", "Delete");
1785
Ed Tanous62598e32023-07-17 17:06:25 -07001786 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
Ninad Palsule97e90da2023-05-17 14:04:52 -05001787 return;
1788 }
1789
1790 messages::created(asyncResp->res);
1791 asyncResp->res.addHeader("Location",
1792 "/redfish/v1/AccountService/Accounts/" + username);
1793}
1794
1795inline void processAfterGetAllGroups(
1796 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1797 const std::string& username, const std::string& password,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001798 const std::string& roleId, bool enabled,
Ninad Palsule9ba73932023-06-01 16:38:57 -05001799 std::optional<std::vector<std::string>> accountTypes,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001800 const std::vector<std::string>& allGroupsList)
Ninad Palsule97e90da2023-05-17 14:04:52 -05001801{
Ninad Palsule3e72c202023-03-27 17:19:55 -05001802 std::vector<std::string> userGroups;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001803 std::vector<std::string> accountTypeUserGroups;
1804
1805 // If user specified account types then convert them to unix user groups
1806 if (accountTypes)
1807 {
1808 if (!getUserGroupFromAccountType(asyncResp->res, *accountTypes,
1809 accountTypeUserGroups))
1810 {
1811 // Problem in mapping Account Types to User Groups, Error already
1812 // logged.
1813 return;
1814 }
1815 }
1816
Ninad Palsule3e72c202023-03-27 17:19:55 -05001817 for (const auto& grp : allGroupsList)
1818 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001819 // If user specified the account type then only accept groups which are
1820 // in the account types group list.
1821 if (!accountTypeUserGroups.empty())
1822 {
1823 bool found = false;
1824 for (const auto& grp1 : accountTypeUserGroups)
1825 {
1826 if (grp == grp1)
1827 {
1828 found = true;
1829 break;
1830 }
1831 }
1832 if (!found)
1833 {
1834 continue;
1835 }
1836 }
1837
Ninad Palsule3e72c202023-03-27 17:19:55 -05001838 // Console access is provided to the user who is a member of
1839 // hostconsole group and has a administrator role. So, set
1840 // hostconsole group only for the administrator.
Ninad Palsule9ba73932023-06-01 16:38:57 -05001841 if ((grp == "hostconsole") && (roleId != "priv-admin"))
Ninad Palsule3e72c202023-03-27 17:19:55 -05001842 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001843 if (!accountTypeUserGroups.empty())
1844 {
Ed Tanous62598e32023-07-17 17:06:25 -07001845 BMCWEB_LOG_ERROR(
1846 "Only administrator can get HostConsole access");
Ninad Palsule9ba73932023-06-01 16:38:57 -05001847 asyncResp->res.result(boost::beast::http::status::bad_request);
1848 return;
1849 }
1850 continue;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001851 }
Ninad Palsule9ba73932023-06-01 16:38:57 -05001852 userGroups.emplace_back(grp);
1853 }
1854
1855 // Make sure user specified groups are valid. This is internal error because
1856 // it some inconsistencies between user manager and bmcweb.
1857 if (!accountTypeUserGroups.empty() &&
1858 accountTypeUserGroups.size() != userGroups.size())
1859 {
1860 messages::internalError(asyncResp->res);
1861 return;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001862 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001863 crow::connections::systemBus->async_method_call(
1864 [asyncResp, username, password](const boost::system::error_code& ec2,
1865 sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001866 processAfterCreateUser(asyncResp, username, password, ec2, m);
1867 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001868 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ninad Palsule3e72c202023-03-27 17:19:55 -05001869 "xyz.openbmc_project.User.Manager", "CreateUser", username, userGroups,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001870 roleId, enabled);
Ninad Palsule97e90da2023-05-17 14:04:52 -05001871}
1872
Ed Tanous1ef4c342022-05-12 16:12:36 -07001873inline void handleAccountCollectionPost(
1874 App& app, const crow::Request& req,
1875 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1876{
1877 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1878 {
1879 return;
1880 }
1881 std::string username;
1882 std::string password;
Ed Tanouse01d0c32023-06-30 13:21:32 -07001883 std::optional<std::string> roleIdJson;
1884 std::optional<bool> enabledJson;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001885 std::optional<std::vector<std::string>> accountTypes;
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001886 if (!json_util::readJsonPatch(
1887 req, asyncResp->res, "UserName", username, "Password", password,
1888 "RoleId", roleIdJson, "Enabled", enabledJson, "AccountTypes",
1889 accountTypes))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001890 {
1891 return;
1892 }
1893
Ed Tanouse01d0c32023-06-30 13:21:32 -07001894 std::string roleId = roleIdJson.value_or("User");
1895 std::string priv = getPrivilegeFromRoleId(roleId);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001896 if (priv.empty())
1897 {
Ed Tanouse01d0c32023-06-30 13:21:32 -07001898 messages::propertyValueNotInList(asyncResp->res, roleId, "RoleId");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001899 return;
1900 }
Asmitha Karunanithi239adf82022-03-25 02:59:03 -05001901 roleId = priv;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001902
Ed Tanouse01d0c32023-06-30 13:21:32 -07001903 bool enabled = enabledJson.value_or(true);
1904
Ed Tanous1ef4c342022-05-12 16:12:36 -07001905 // Reading AllGroups property
1906 sdbusplus::asio::getProperty<std::vector<std::string>>(
1907 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1908 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager",
1909 "AllGroups",
Ninad Palsule9ba73932023-06-01 16:38:57 -05001910 [asyncResp, username, password{std::move(password)}, roleId, enabled,
1911 accountTypes](const boost::system::error_code& ec,
1912 const std::vector<std::string>& allGroupsList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001913 if (ec)
1914 {
Gunnar Millsa0735a42024-09-06 12:51:11 -05001915 BMCWEB_LOG_ERROR("D-Bus response error {}", ec);
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001916 messages::internalError(asyncResp->res);
1917 return;
1918 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001919
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001920 if (allGroupsList.empty())
1921 {
1922 messages::internalError(asyncResp->res);
1923 return;
1924 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07001925
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001926 processAfterGetAllGroups(asyncResp, username, password, roleId,
1927 enabled, accountTypes, allGroupsList);
1928 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001929}
1930
1931inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001932 handleAccountHead(App& app, const crow::Request& req,
1933 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1934 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001935{
1936 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1937 {
1938 return;
1939 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001940 asyncResp->res.addHeader(
1941 boost::beast::http::field::link,
1942 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1943}
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001944
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001945inline void
1946 handleAccountGet(App& app, const crow::Request& req,
1947 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1948 const std::string& accountName)
1949{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001950 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1951 {
1952 return;
1953 }
1954 asyncResp->res.addHeader(
1955 boost::beast::http::field::link,
1956 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1957
Ed Tanous25b54db2024-04-17 15:40:31 -07001958 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
1959 {
1960 // If authentication is disabled, there are no user accounts
1961 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1962 accountName);
1963 return;
1964 }
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001965
Ed Tanous1ef4c342022-05-12 16:12:36 -07001966 if (req.session == nullptr)
1967 {
1968 messages::internalError(asyncResp->res);
1969 return;
1970 }
1971 if (req.session->username != accountName)
1972 {
1973 // At this point we've determined that the user is trying to
1974 // modify a user that isn't them. We need to verify that they
1975 // have permissions to modify other users, so re-run the auth
1976 // check with the same permissions, minus ConfigureSelf.
1977 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001978 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001979 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
1980 "ConfigureManager"};
1981 if (!effectiveUserPrivileges.isSupersetOf(
1982 requiredPermissionsToChangeNonSelf))
1983 {
Ed Tanous62598e32023-07-17 17:06:25 -07001984 BMCWEB_LOG_DEBUG("GET Account denied access");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001985 messages::insufficientPrivilege(asyncResp->res);
1986 return;
1987 }
1988 }
1989
George Liu5eb468d2023-06-20 17:03:24 +08001990 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
1991 dbus::utility::getManagedObjects(
1992 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001993 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001994 accountName](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001995 const dbus::utility::ManagedObjectType& users) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001996 if (ec)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001997 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001998 messages::internalError(asyncResp->res);
1999 return;
2000 }
2001 const auto userIt = std::ranges::find_if(
2002 users,
2003 [accountName](
2004 const std::pair<sdbusplus::message::object_path,
2005 dbus::utility::DBusInterfacesMap>& user) {
2006 return accountName == user.first.filename();
2007 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002008
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002009 if (userIt == users.end())
2010 {
2011 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2012 accountName);
2013 return;
2014 }
2015
2016 asyncResp->res.jsonValue["@odata.type"] =
2017 "#ManagerAccount.v1_7_0.ManagerAccount";
2018 asyncResp->res.jsonValue["Name"] = "User Account";
2019 asyncResp->res.jsonValue["Description"] = "User Account";
2020 asyncResp->res.jsonValue["Password"] = nullptr;
2021 asyncResp->res.jsonValue["StrictAccountTypes"] = true;
2022
2023 for (const auto& interface : userIt->second)
2024 {
2025 if (interface.first == "xyz.openbmc_project.User.Attributes")
2026 {
2027 for (const auto& property : interface.second)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002028 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002029 if (property.first == "UserEnabled")
Ed Tanous1ef4c342022-05-12 16:12:36 -07002030 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002031 const bool* userEnabled =
2032 std::get_if<bool>(&property.second);
2033 if (userEnabled == nullptr)
2034 {
2035 BMCWEB_LOG_ERROR("UserEnabled wasn't a bool");
2036 messages::internalError(asyncResp->res);
2037 return;
2038 }
2039 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
Ed Tanous1ef4c342022-05-12 16:12:36 -07002040 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002041 else if (property.first == "UserLockedForFailedAttempt")
Abhishek Patelc7229812022-02-01 10:07:15 -06002042 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002043 const bool* userLocked =
2044 std::get_if<bool>(&property.second);
2045 if (userLocked == nullptr)
2046 {
2047 BMCWEB_LOG_ERROR("UserLockedForF"
2048 "ailedAttempt "
2049 "wasn't a bool");
2050 messages::internalError(asyncResp->res);
2051 return;
2052 }
2053 asyncResp->res.jsonValue["Locked"] = *userLocked;
2054 nlohmann::json::array_t allowed;
2055 // can only unlock accounts
2056 allowed.emplace_back("false");
2057 asyncResp->res
2058 .jsonValue["Locked@Redfish.AllowableValues"] =
2059 std::move(allowed);
Abhishek Patelc7229812022-02-01 10:07:15 -06002060 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002061 else if (property.first == "UserPrivilege")
Abhishek Patelc7229812022-02-01 10:07:15 -06002062 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002063 const std::string* userPrivPtr =
2064 std::get_if<std::string>(&property.second);
2065 if (userPrivPtr == nullptr)
2066 {
2067 BMCWEB_LOG_ERROR("UserPrivilege wasn't a "
2068 "string");
2069 messages::internalError(asyncResp->res);
2070 return;
2071 }
2072 std::string role =
2073 getRoleIdFromPrivilege(*userPrivPtr);
2074 if (role.empty())
2075 {
2076 BMCWEB_LOG_ERROR("Invalid user role");
2077 messages::internalError(asyncResp->res);
2078 return;
2079 }
2080 asyncResp->res.jsonValue["RoleId"] = role;
2081
2082 nlohmann::json& roleEntry =
2083 asyncResp->res.jsonValue["Links"]["Role"];
2084 roleEntry["@odata.id"] = boost::urls::format(
2085 "/redfish/v1/AccountService/Roles/{}", role);
2086 }
2087 else if (property.first == "UserPasswordExpired")
2088 {
2089 const bool* userPasswordExpired =
2090 std::get_if<bool>(&property.second);
2091 if (userPasswordExpired == nullptr)
2092 {
2093 BMCWEB_LOG_ERROR(
2094 "UserPasswordExpired wasn't a bool");
2095 messages::internalError(asyncResp->res);
2096 return;
2097 }
2098 asyncResp->res.jsonValue["PasswordChangeRequired"] =
2099 *userPasswordExpired;
2100 }
2101 else if (property.first == "UserGroups")
2102 {
2103 const std::vector<std::string>* userGroups =
2104 std::get_if<std::vector<std::string>>(
2105 &property.second);
2106 if (userGroups == nullptr)
2107 {
2108 BMCWEB_LOG_ERROR(
2109 "userGroups wasn't a string vector");
2110 messages::internalError(asyncResp->res);
2111 return;
2112 }
2113 if (!translateUserGroup(*userGroups,
2114 asyncResp->res))
2115 {
2116 BMCWEB_LOG_ERROR("userGroups mapping failed");
2117 messages::internalError(asyncResp->res);
2118 return;
2119 }
Abhishek Patelc7229812022-02-01 10:07:15 -06002120 }
2121 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002122 }
2123 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002124
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002125 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
2126 "/redfish/v1/AccountService/Accounts/{}", accountName);
2127 asyncResp->res.jsonValue["Id"] = accountName;
2128 asyncResp->res.jsonValue["UserName"] = accountName;
2129 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002130}
2131
2132inline void
Gunnar Mills20fc3072023-01-27 15:13:36 -06002133 handleAccountDelete(App& app, const crow::Request& req,
2134 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2135 const std::string& username)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002136{
2137 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2138 {
2139 return;
2140 }
2141
Ed Tanous25b54db2024-04-17 15:40:31 -07002142 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2143 {
2144 // If authentication is disabled, there are no user accounts
2145 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2146 return;
2147 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002148 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
2149 tempObjPath /= username;
2150 const std::string userPath(tempObjPath);
2151
2152 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002153 [asyncResp, username](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002154 if (ec)
2155 {
2156 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
2157 username);
2158 return;
2159 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002160
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002161 messages::accountRemoved(asyncResp->res);
2162 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002163 "xyz.openbmc_project.User.Manager", userPath,
2164 "xyz.openbmc_project.Object.Delete", "Delete");
2165}
2166
2167inline void
2168 handleAccountPatch(App& app, const crow::Request& req,
2169 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2170 const std::string& username)
2171{
2172 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2173 {
2174 return;
2175 }
Ed Tanous25b54db2024-04-17 15:40:31 -07002176 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2177 {
2178 // If authentication is disabled, there are no user accounts
2179 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2180 return;
2181 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002182 std::optional<std::string> newUserName;
2183 std::optional<std::string> password;
2184 std::optional<bool> enabled;
2185 std::optional<std::string> roleId;
2186 std::optional<bool> locked;
Abhishek Patel58345852022-02-02 08:54:25 -06002187 std::optional<std::vector<std::string>> accountTypes;
2188
Ed Tanous1ef4c342022-05-12 16:12:36 -07002189 if (req.session == nullptr)
2190 {
2191 messages::internalError(asyncResp->res);
2192 return;
2193 }
2194
Ed Tanous2b9c1df2024-04-06 13:52:01 -07002195 bool userSelf = (username == req.session->username);
2196
Ed Tanous1ef4c342022-05-12 16:12:36 -07002197 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002198 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002199 Privileges configureUsers = {"ConfigureUsers"};
2200 bool userHasConfigureUsers =
2201 effectiveUserPrivileges.isSupersetOf(configureUsers);
2202 if (userHasConfigureUsers)
2203 {
2204 // Users with ConfigureUsers can modify for all users
Abhishek Patel58345852022-02-02 08:54:25 -06002205 if (!json_util::readJsonPatch(
2206 req, asyncResp->res, "UserName", newUserName, "Password",
2207 password, "RoleId", roleId, "Enabled", enabled, "Locked",
2208 locked, "AccountTypes", accountTypes))
Ed Tanous1ef4c342022-05-12 16:12:36 -07002209 {
2210 return;
2211 }
2212 }
2213 else
2214 {
2215 // ConfigureSelf accounts can only modify their own account
Abhishek Patel58345852022-02-02 08:54:25 -06002216 if (!userSelf)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002217 {
2218 messages::insufficientPrivilege(asyncResp->res);
2219 return;
2220 }
2221
2222 // ConfigureSelf accounts can only modify their password
2223 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
2224 password))
2225 {
2226 return;
2227 }
2228 }
2229
2230 // if user name is not provided in the patch method or if it
2231 // matches the user name in the URI, then we are treating it as
2232 // updating user properties other then username. If username
2233 // provided doesn't match the URI, then we are treating this as
2234 // user rename request.
2235 if (!newUserName || (newUserName.value() == username))
2236 {
2237 updateUserProperties(asyncResp, username, password, enabled, roleId,
Ravi Tejae518ef32024-05-16 10:33:08 -05002238 locked, accountTypes, userSelf, req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002239 return;
2240 }
2241 crow::connections::systemBus->async_method_call(
2242 [asyncResp, username, password(std::move(password)),
2243 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Ravi Tejae518ef32024-05-16 10:33:08 -05002244 locked, userSelf, req, accountTypes(std::move(accountTypes))](
Ed Tanouse81de512023-06-27 17:07:00 -07002245 const boost::system::error_code& ec, sdbusplus::message_t& m) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002246 if (ec)
2247 {
2248 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
2249 username);
2250 return;
2251 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002252
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002253 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
2254 locked, accountTypes, userSelf, req.session);
2255 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002256 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
2257 "xyz.openbmc_project.User.Manager", "RenameUser", username,
2258 *newUserName);
2259}
2260
Ed Tanous6c51eab2021-06-03 12:30:29 -07002261inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07002262{
Ed Tanous6c51eab2021-06-03 12:30:29 -07002263 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002264 .privileges(redfish::privileges::headAccountService)
2265 .methods(boost::beast::http::verb::head)(
2266 std::bind_front(handleAccountServiceHead, std::ref(app)));
2267
2268 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07002269 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002270 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002271 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002272
Ed Tanousf5ffd802021-07-19 10:55:33 -07002273 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06002274 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07002275 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002276 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07002277
Ed Tanous1aa375b2024-04-13 11:51:10 -07002278 BMCWEB_ROUTE(
2279 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002280 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002281 .privileges(redfish::privileges::headCertificateCollection)
2282 .methods(boost::beast::http::verb::head)(std::bind_front(
2283 handleAccountServiceClientCertificatesHead, std::ref(app)));
2284
2285 BMCWEB_ROUTE(
2286 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002287 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002288 .privileges(redfish::privileges::getCertificateCollection)
2289 .methods(boost::beast::http::verb::get)(std::bind_front(
2290 handleAccountServiceClientCertificatesGet, std::ref(app)));
2291
2292 BMCWEB_ROUTE(
2293 app,
Ed Tanouse9f12012024-10-08 13:37:07 -07002294 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
Ed Tanous1aa375b2024-04-13 11:51:10 -07002295 .privileges(redfish::privileges::headCertificate)
2296 .methods(boost::beast::http::verb::head)(std::bind_front(
2297 handleAccountServiceClientCertificatesInstanceHead, std::ref(app)));
2298
2299 BMCWEB_ROUTE(
2300 app,
2301 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
2302 .privileges(redfish::privileges::getCertificate)
2303 .methods(boost::beast::http::verb::get)(std::bind_front(
2304 handleAccountServiceClientCertificatesInstanceGet, std::ref(app)));
2305
Ed Tanous6c51eab2021-06-03 12:30:29 -07002306 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002307 .privileges(redfish::privileges::headManagerAccountCollection)
2308 .methods(boost::beast::http::verb::head)(
2309 std::bind_front(handleAccountCollectionHead, std::ref(app)));
2310
2311 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002312 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002313 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002314 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07002315
Ed Tanous6c51eab2021-06-03 12:30:29 -07002316 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002317 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002318 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002319 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07002320
2321 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002322 .privileges(redfish::privileges::headManagerAccount)
2323 .methods(boost::beast::http::verb::head)(
2324 std::bind_front(handleAccountHead, std::ref(app)));
2325
2326 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07002327 .privileges(redfish::privileges::getManagerAccount)
2328 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002329 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002330
2331 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002332 // TODO this privilege should be using the generated endpoints, but
2333 // because of the special handling of ConfigureSelf, it's not able to
2334 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07002335 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
2336 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002337 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002338
2339 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002340 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002341 .methods(boost::beast::http::verb::delete_)(
Gunnar Mills20fc3072023-01-27 15:13:36 -06002342 std::bind_front(handleAccountDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002343}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002344
Ed Tanous1abe55e2018-09-05 08:30:59 -07002345} // namespace redfish