blob: aa0678272f4a19591f966c9aca52f75d9e7fb177 [file] [log] [blame]
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010017
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080018#include "app.hpp"
Ed Tanous1aa375b2024-04-13 11:51:10 -070019#include "certificate_service.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080020#include "dbus_utility.hpp"
21#include "error_messages.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070022#include "generated/enums/account_service.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080023#include "persistent_data.hpp"
24#include "query.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070025#include "registries/privilege_registry.hpp"
Ed Tanous3281bcf2024-06-25 16:02:05 -070026#include "sessions.hpp"
Ed Tanous1aa375b2024-04-13 11:51:10 -070027#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080028#include "utils/dbus_utils.hpp"
29#include "utils/json_utils.hpp"
Ed Tanous0ec8b832022-03-14 14:56:47 -070030
Ed Tanous1aa375b2024-04-13 11:51:10 -070031#include <boost/url/format.hpp>
32#include <boost/url/url.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070033#include <sdbusplus/asio/property.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020034#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050035
George Liu2b731192023-01-11 16:27:13 +080036#include <array>
Ed Tanous1aa375b2024-04-13 11:51:10 -070037#include <memory>
Abhishek Patelc7229812022-02-01 10:07:15 -060038#include <optional>
Ed Tanous3544d2a2023-08-06 18:12:20 -070039#include <ranges>
Abhishek Patelc7229812022-02-01 10:07:15 -060040#include <string>
George Liu2b731192023-01-11 16:27:13 +080041#include <string_view>
Ed Tanous20fa6a22024-05-20 18:02:58 -070042#include <utility>
Abhishek Patelc7229812022-02-01 10:07:15 -060043#include <vector>
44
Ed Tanous1abe55e2018-09-05 08:30:59 -070045namespace redfish
46{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010047
Ed Tanous23a21a12020-07-25 04:45:05 +000048constexpr const char* ldapConfigObjectName =
Ratan Gupta6973a582018-12-13 18:25:44 +053049 "/xyz/openbmc_project/user/ldap/openldap";
Ed Tanous2c70f802020-09-28 14:29:23 -070050constexpr const char* adConfigObject =
Ratan Guptaab828d72019-04-22 14:18:33 +053051 "/xyz/openbmc_project/user/ldap/active_directory";
52
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +053053constexpr const char* rootUserDbusPath = "/xyz/openbmc_project/user/";
Ratan Gupta6973a582018-12-13 18:25:44 +053054constexpr const char* ldapRootObject = "/xyz/openbmc_project/user/ldap";
55constexpr const char* ldapDbusService = "xyz.openbmc_project.Ldap.Config";
56constexpr const char* ldapConfigInterface =
57 "xyz.openbmc_project.User.Ldap.Config";
58constexpr const char* ldapCreateInterface =
59 "xyz.openbmc_project.User.Ldap.Create";
60constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
Ratan Gupta06785242019-07-26 22:30:16 +053061constexpr const char* ldapPrivMapperInterface =
62 "xyz.openbmc_project.User.PrivilegeMapper";
Ratan Gupta6973a582018-12-13 18:25:44 +053063
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060064struct LDAPRoleMapData
65{
66 std::string groupName;
67 std::string privilege;
68};
69
Ratan Gupta6973a582018-12-13 18:25:44 +053070struct LDAPConfigData
71{
Ed Tanous47f29342024-03-19 12:18:06 -070072 std::string uri;
73 std::string bindDN;
74 std::string baseDN;
75 std::string searchScope;
76 std::string serverType;
Ratan Gupta6973a582018-12-13 18:25:44 +053077 bool serviceEnabled = false;
Ed Tanous47f29342024-03-19 12:18:06 -070078 std::string userNameAttribute;
79 std::string groupAttribute;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060080 std::vector<std::pair<std::string, LDAPRoleMapData>> groupRoleList;
Ratan Gupta6973a582018-12-13 18:25:44 +053081};
82
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060083inline std::string getRoleIdFromPrivilege(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +053084{
85 if (role == "priv-admin")
86 {
87 return "Administrator";
88 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070089 if (role == "priv-user")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053090 {
AppaRao Pulic80fee52019-10-16 14:49:36 +053091 return "ReadOnly";
AppaRao Puli84e12cb2018-10-11 01:28:15 +053092 }
Ed Tanous3174e4d2020-10-07 11:41:22 -070093 if (role == "priv-operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +053094 {
95 return "Operator";
96 }
97 return "";
98}
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -060099inline std::string getPrivilegeFromRoleId(std::string_view role)
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530100{
101 if (role == "Administrator")
102 {
103 return "priv-admin";
104 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700105 if (role == "ReadOnly")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530106 {
107 return "priv-user";
108 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700109 if (role == "Operator")
AppaRao Puli84e12cb2018-10-11 01:28:15 +0530110 {
111 return "priv-operator";
112 }
113 return "";
114}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700115
Abhishek Patelc7229812022-02-01 10:07:15 -0600116/**
117 * @brief Maps user group names retrieved from D-Bus object to
118 * Account Types.
119 *
120 * @param[in] userGroups List of User groups
121 * @param[out] res AccountTypes populated
122 *
123 * @return true in case of success, false if UserGroups contains
124 * invalid group name(s).
125 */
126inline bool translateUserGroup(const std::vector<std::string>& userGroups,
127 crow::Response& res)
128{
129 std::vector<std::string> accountTypes;
130 for (const auto& userGroup : userGroups)
131 {
132 if (userGroup == "redfish")
133 {
134 accountTypes.emplace_back("Redfish");
135 accountTypes.emplace_back("WebUI");
136 }
137 else if (userGroup == "ipmi")
138 {
139 accountTypes.emplace_back("IPMI");
140 }
141 else if (userGroup == "ssh")
142 {
Abhishek Patelc7229812022-02-01 10:07:15 -0600143 accountTypes.emplace_back("ManagerConsole");
144 }
Ninad Palsule3e72c202023-03-27 17:19:55 -0500145 else if (userGroup == "hostconsole")
146 {
147 // The hostconsole group controls who can access the host console
148 // port via ssh and websocket.
149 accountTypes.emplace_back("HostConsole");
150 }
Abhishek Patelc7229812022-02-01 10:07:15 -0600151 else if (userGroup == "web")
152 {
153 // 'web' is one of the valid groups in the UserGroups property of
154 // the user account in the D-Bus object. This group is currently not
155 // doing anything, and is considered to be equivalent to 'redfish'.
156 // 'redfish' user group is mapped to 'Redfish'and 'WebUI'
157 // AccountTypes, so do nothing here...
158 }
159 else
160 {
Ed Tanous8ece0e42024-01-02 13:16:50 -0800161 // Invalid user group name. Caller throws an exception.
Abhishek Patelc7229812022-02-01 10:07:15 -0600162 return false;
163 }
164 }
165
166 res.jsonValue["AccountTypes"] = std::move(accountTypes);
167 return true;
168}
169
Abhishek Patel58345852022-02-02 08:54:25 -0600170/**
171 * @brief Builds User Groups from the Account Types
172 *
173 * @param[in] asyncResp Async Response
174 * @param[in] accountTypes List of Account Types
175 * @param[out] userGroups List of User Groups mapped from Account Types
176 *
177 * @return true if Account Types mapped to User Groups, false otherwise.
178 */
179inline bool
180 getUserGroupFromAccountType(crow::Response& res,
181 const std::vector<std::string>& accountTypes,
182 std::vector<std::string>& userGroups)
183{
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) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700387 if (ec)
388 {
Ed Tanous62598e32023-07-17 17:06:25 -0700389 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700390 messages::internalError(asyncResp->res);
391 return;
392 }
Patrick Williams89492a12023-05-10 07:51:34 -0500393 asyncResp->res.jsonValue[serverType]["RemoteRoleMapping"]
394 [index] = nullptr;
Patrick Williams5a39f772023-10-20 11:20:21 -0500395 },
Ratan Gupta06785242019-07-26 22:30:16 +0530396 ldapDbusService, roleMapObjData[index].first,
397 "xyz.openbmc_project.Object.Delete", "Delete");
398 }
399 else
400 {
Ed Tanous62598e32023-07-17 17:06:25 -0700401 BMCWEB_LOG_ERROR("Can't delete the object");
Ed Tanousc1019822024-03-06 12:54:38 -0800402 messages::propertyValueTypeError(asyncResp->res, "null",
Ed Tanous2e8c4bd2022-06-27 12:59:12 -0700403 "RemoteRoleMapping/" +
404 std::to_string(index));
Ratan Gupta06785242019-07-26 22:30:16 +0530405 return;
406 }
407 }
Ed Tanousc1019822024-03-06 12:54:38 -0800408 else if (obj->empty())
Ratan Gupta06785242019-07-26 22:30:16 +0530409 {
410 // Don't do anything for the empty objects,parse next json
411 // eg {"RemoteRoleMapping",[{}]}
412 }
413 else
414 {
415 // update/create the object
416 std::optional<std::string> remoteGroup;
417 std::optional<std::string> localRole;
418
Ed Tanousc1019822024-03-06 12:54:38 -0800419 if (!json_util::readJsonObject(*obj, asyncResp->res, "RemoteGroup",
420 remoteGroup, "LocalRole", localRole))
Ratan Gupta06785242019-07-26 22:30:16 +0530421 {
422 continue;
423 }
424
425 // Update existing RoleMapping Object
426 if (index < roleMapObjData.size())
427 {
Ed Tanous62598e32023-07-17 17:06:25 -0700428 BMCWEB_LOG_DEBUG("Update Role Map Object");
Ratan Gupta06785242019-07-26 22:30:16 +0530429 // If "RemoteGroup" info is provided
430 if (remoteGroup)
431 {
Ed Tanousd02aad32024-02-13 14:43:34 -0800432 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530433 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800434 std::format("RemoteRoleMapping/{}/RemoteGroup", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530435 ldapDbusService, roleMapObjData[index].first,
436 "xyz.openbmc_project.User.PrivilegeMapperEntry",
437 "GroupName", *remoteGroup);
Ratan Gupta06785242019-07-26 22:30:16 +0530438 }
439
440 // If "LocalRole" info is provided
441 if (localRole)
442 {
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500443 std::string priv = getPrivilegeFromRoleId(*localRole);
444 if (priv.empty())
445 {
446 messages::propertyValueNotInList(
447 asyncResp->res, *localRole,
448 std::format("RemoteRoleMapping/{}/LocalRole",
449 index));
450 return;
451 }
Ed Tanousd02aad32024-02-13 14:43:34 -0800452 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +0530453 asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800454 std::format("RemoteRoleMapping/{}/LocalRole", index),
Ginu Georgee93abac2024-06-14 17:35:27 +0530455 ldapDbusService, roleMapObjData[index].first,
456 "xyz.openbmc_project.User.PrivilegeMapperEntry",
Ravi Teja6d0b80b2024-07-28 06:09:15 -0500457 "Privilege", priv);
Ratan Gupta06785242019-07-26 22:30:16 +0530458 }
459 }
460 // Create a new RoleMapping Object.
461 else
462 {
Ed Tanous62598e32023-07-17 17:06:25 -0700463 BMCWEB_LOG_DEBUG(
464 "setRoleMappingProperties: Creating new Object");
Patrick Williams89492a12023-05-10 07:51:34 -0500465 std::string pathString = "RemoteRoleMapping/" +
466 std::to_string(index);
Ratan Gupta06785242019-07-26 22:30:16 +0530467
468 if (!localRole)
469 {
470 messages::propertyMissing(asyncResp->res,
471 pathString + "/LocalRole");
472 continue;
473 }
474 if (!remoteGroup)
475 {
476 messages::propertyMissing(asyncResp->res,
477 pathString + "/RemoteGroup");
478 continue;
479 }
480
481 std::string dbusObjectPath;
482 if (serverType == "ActiveDirectory")
483 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700484 dbusObjectPath = adConfigObject;
Ratan Gupta06785242019-07-26 22:30:16 +0530485 }
486 else if (serverType == "LDAP")
487 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000488 dbusObjectPath = ldapConfigObjectName;
Ratan Gupta06785242019-07-26 22:30:16 +0530489 }
490
Ed Tanous62598e32023-07-17 17:06:25 -0700491 BMCWEB_LOG_DEBUG("Remote Group={},LocalRole={}", *remoteGroup,
492 *localRole);
Ratan Gupta06785242019-07-26 22:30:16 +0530493
494 crow::connections::systemBus->async_method_call(
Ed Tanous271584a2019-07-09 16:24:22 -0700495 [asyncResp, serverType, localRole,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800496 remoteGroup](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700497 if (ec)
498 {
Ed Tanous62598e32023-07-17 17:06:25 -0700499 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700500 messages::internalError(asyncResp->res);
501 return;
502 }
503 nlohmann::json& remoteRoleJson =
504 asyncResp->res
505 .jsonValue[serverType]["RemoteRoleMapping"];
506 nlohmann::json::object_t roleMapEntry;
507 roleMapEntry["LocalRole"] = *localRole;
508 roleMapEntry["RemoteGroup"] = *remoteGroup;
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500509 remoteRoleJson.emplace_back(std::move(roleMapEntry));
Patrick Williams5a39f772023-10-20 11:20:21 -0500510 },
Ratan Gupta06785242019-07-26 22:30:16 +0530511 ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
Ed Tanous3174e4d2020-10-07 11:41:22 -0700512 "Create", *remoteGroup,
Ratan Gupta06785242019-07-26 22:30:16 +0530513 getPrivilegeFromRoleId(std::move(*localRole)));
514 }
515 }
516 }
517}
518
519/**
Ratan Gupta6973a582018-12-13 18:25:44 +0530520 * Function that retrieves all properties for LDAP config object
521 * into JSON
522 */
523template <typename CallbackFunc>
524inline void getLDAPConfigData(const std::string& ldapType,
525 CallbackFunc&& callback)
526{
George Liu2b731192023-01-11 16:27:13 +0800527 constexpr std::array<std::string_view, 2> interfaces = {
528 ldapEnableInterface, ldapConfigInterface};
Ratan Gupta6973a582018-12-13 18:25:44 +0530529
George Liu2b731192023-01-11 16:27:13 +0800530 dbus::utility::getDbusObject(
531 ldapConfigObjectName, interfaces,
Ed Tanous8cb2c022024-03-27 16:31:46 -0700532 [callback = std::forward<CallbackFunc>(callback),
Ed Tanousc1019822024-03-06 12:54:38 -0800533 ldapType](const boost::system::error_code& ec,
534 const dbus::utility::MapperGetObject& resp) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700535 if (ec || resp.empty())
536 {
Carson Labradobf2dded2023-08-10 00:37:06 +0000537 BMCWEB_LOG_WARNING(
Ed Tanous62598e32023-07-17 17:06:25 -0700538 "DBUS response error during getting of service name: {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700539 LDAPConfigData empty{};
540 callback(false, empty, ldapType);
541 return;
542 }
543 std::string service = resp.begin()->first;
George Liu5eb468d2023-06-20 17:03:24 +0800544 sdbusplus::message::object_path path(ldapRootObject);
545 dbus::utility::getManagedObjects(
546 service, path,
Ed Tanousc1019822024-03-06 12:54:38 -0800547 [callback, ldapType](
548 const boost::system::error_code& ec2,
549 const dbus::utility::ManagedObjectType& ldapObjects) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700550 LDAPConfigData confData{};
Ed Tanous8b242752023-06-27 17:17:13 -0700551 if (ec2)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600552 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700553 callback(false, confData, ldapType);
Carson Labradobf2dded2023-08-10 00:37:06 +0000554 BMCWEB_LOG_WARNING("D-Bus responses error: {}", ec2);
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600555 return;
556 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600557
Ed Tanous002d39b2022-05-31 08:59:27 -0700558 std::string ldapDbusType;
559 std::string searchString;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600560
Ed Tanous002d39b2022-05-31 08:59:27 -0700561 if (ldapType == "LDAP")
562 {
563 ldapDbusType =
564 "xyz.openbmc_project.User.Ldap.Config.Type.OpenLdap";
565 searchString = "openldap";
566 }
567 else if (ldapType == "ActiveDirectory")
568 {
569 ldapDbusType =
570 "xyz.openbmc_project.User.Ldap.Config.Type.ActiveDirectory";
571 searchString = "active_directory";
572 }
573 else
574 {
Ed Tanous62598e32023-07-17 17:06:25 -0700575 BMCWEB_LOG_ERROR("Can't get the DbusType for the given type={}",
576 ldapType);
Ed Tanous002d39b2022-05-31 08:59:27 -0700577 callback(false, confData, ldapType);
578 return;
579 }
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600580
Ed Tanous002d39b2022-05-31 08:59:27 -0700581 std::string ldapEnableInterfaceStr = ldapEnableInterface;
582 std::string ldapConfigInterfaceStr = ldapConfigInterface;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600583
Ed Tanous002d39b2022-05-31 08:59:27 -0700584 for (const auto& object : ldapObjects)
585 {
586 // let's find the object whose ldap type is equal to the
587 // given type
588 if (object.first.str.find(searchString) == std::string::npos)
589 {
590 continue;
591 }
592
593 for (const auto& interface : object.second)
594 {
595 if (interface.first == ldapEnableInterfaceStr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600596 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700597 // rest of the properties are string.
598 for (const auto& property : interface.second)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600599 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700600 if (property.first == "Enabled")
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600601 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700602 const bool* value =
603 std::get_if<bool>(&property.second);
604 if (value == nullptr)
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600605 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700606 continue;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600607 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700608 confData.serviceEnabled = *value;
609 break;
Nagaraju Goruganti54fc5872019-01-30 05:11:00 -0600610 }
611 }
612 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700613 else if (interface.first == ldapConfigInterfaceStr)
614 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700615 for (const auto& property : interface.second)
616 {
617 const std::string* strValue =
618 std::get_if<std::string>(&property.second);
619 if (strValue == nullptr)
620 {
621 continue;
622 }
623 if (property.first == "LDAPServerURI")
624 {
625 confData.uri = *strValue;
626 }
627 else if (property.first == "LDAPBindDN")
628 {
629 confData.bindDN = *strValue;
630 }
631 else if (property.first == "LDAPBaseDN")
632 {
633 confData.baseDN = *strValue;
634 }
635 else if (property.first == "LDAPSearchScope")
636 {
637 confData.searchScope = *strValue;
638 }
639 else if (property.first == "GroupNameAttribute")
640 {
641 confData.groupAttribute = *strValue;
642 }
643 else if (property.first == "UserNameAttribute")
644 {
645 confData.userNameAttribute = *strValue;
646 }
647 else if (property.first == "LDAPType")
648 {
649 confData.serverType = *strValue;
650 }
651 }
652 }
653 else if (interface.first ==
654 "xyz.openbmc_project.User.PrivilegeMapperEntry")
655 {
656 LDAPRoleMapData roleMapData{};
657 for (const auto& property : interface.second)
658 {
659 const std::string* strValue =
660 std::get_if<std::string>(&property.second);
661
662 if (strValue == nullptr)
663 {
664 continue;
665 }
666
667 if (property.first == "GroupName")
668 {
669 roleMapData.groupName = *strValue;
670 }
671 else if (property.first == "Privilege")
672 {
673 roleMapData.privilege = *strValue;
674 }
675 }
676
677 confData.groupRoleList.emplace_back(object.first.str,
678 roleMapData);
679 }
680 }
681 }
682 callback(true, confData, ldapType);
George Liu2b731192023-01-11 16:27:13 +0800683 });
Patrick Williams5a39f772023-10-20 11:20:21 -0500684 });
Ratan Gupta6973a582018-12-13 18:25:44 +0530685}
686
Ed Tanous6c51eab2021-06-03 12:30:29 -0700687/**
Ed Tanous6c51eab2021-06-03 12:30:29 -0700688 * @brief updates the LDAP server address and updates the
689 json response with the new value.
690 * @param serviceAddressList address to be updated.
691 * @param asyncResp pointer to the JSON response
692 * @param ldapServerElementName Type of LDAP
693 server(openLDAP/ActiveDirectory)
694 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530695
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700696inline void handleServiceAddressPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700697 const std::vector<std::string>& serviceAddressList,
698 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
699 const std::string& ldapServerElementName,
700 const std::string& ldapConfigObject)
701{
Ginu Georgee93abac2024-06-14 17:35:27 +0530702 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceAddress",
703 ldapDbusService, ldapConfigObject, ldapConfigInterface,
704 "LDAPServerURI", serviceAddressList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700705}
706/**
707 * @brief updates the LDAP Bind DN and updates the
708 json response with the new value.
709 * @param username name of the user which needs to be updated.
710 * @param asyncResp pointer to the JSON response
711 * @param ldapServerElementName Type of LDAP
712 server(openLDAP/ActiveDirectory)
713 */
Ratan Gupta8a07d282019-03-16 08:33:47 +0530714
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700715inline void
716 handleUserNamePatch(const std::string& username,
717 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
718 const std::string& ldapServerElementName,
719 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700720{
Ginu Georgee93abac2024-06-14 17:35:27 +0530721 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800722 ldapServerElementName + "/Authentication/Username",
Ginu Georgee93abac2024-06-14 17:35:27 +0530723 ldapDbusService, ldapConfigObject, ldapConfigInterface,
724 "LDAPBindDN", username);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700725}
726
727/**
728 * @brief updates the LDAP password
729 * @param password : ldap password which needs to be updated.
730 * @param asyncResp pointer to the JSON response
731 * @param ldapServerElementName Type of LDAP
732 * server(openLDAP/ActiveDirectory)
733 */
734
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700735inline void
736 handlePasswordPatch(const std::string& password,
737 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
738 const std::string& ldapServerElementName,
739 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700740{
Ginu Georgee93abac2024-06-14 17:35:27 +0530741 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800742 ldapServerElementName + "/Authentication/Password",
Ginu Georgee93abac2024-06-14 17:35:27 +0530743 ldapDbusService, ldapConfigObject, ldapConfigInterface,
744 "LDAPBindDNPassword", password);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700745}
746
747/**
748 * @brief updates the LDAP BaseDN and updates the
749 json response with the new value.
750 * @param baseDNList baseDN list which needs to be updated.
751 * @param asyncResp pointer to the JSON response
752 * @param ldapServerElementName Type of LDAP
753 server(openLDAP/ActiveDirectory)
754 */
755
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700756inline void
757 handleBaseDNPatch(const std::vector<std::string>& baseDNList,
758 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
759 const std::string& ldapServerElementName,
760 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700761{
Ginu Georgee93abac2024-06-14 17:35:27 +0530762 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800763 ldapServerElementName +
764 "/LDAPService/SearchSettings/BaseDistinguishedNames",
Ginu Georgee93abac2024-06-14 17:35:27 +0530765 ldapDbusService, ldapConfigObject, ldapConfigInterface,
766 "LDAPBaseDN", baseDNList.front());
Ed Tanous6c51eab2021-06-03 12:30:29 -0700767}
768/**
769 * @brief updates the LDAP user name attribute and updates the
770 json response with the new value.
771 * @param userNameAttribute attribute to be updated.
772 * @param asyncResp pointer to the JSON response
773 * @param ldapServerElementName Type of LDAP
774 server(openLDAP/ActiveDirectory)
775 */
776
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700777inline void
778 handleUserNameAttrPatch(const std::string& userNameAttribute,
779 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
780 const std::string& ldapServerElementName,
781 const std::string& ldapConfigObject)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700782{
Ginu Georgee93abac2024-06-14 17:35:27 +0530783 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800784 ldapServerElementName +
785 "LDAPService/SearchSettings/UsernameAttribute",
Ginu Georgee93abac2024-06-14 17:35:27 +0530786 ldapDbusService, ldapConfigObject, ldapConfigInterface,
787 "UserNameAttribute", userNameAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700788}
789/**
790 * @brief updates the LDAP group attribute and updates the
791 json response with the new value.
792 * @param groupsAttribute attribute to be updated.
793 * @param asyncResp pointer to the JSON response
794 * @param ldapServerElementName Type of LDAP
795 server(openLDAP/ActiveDirectory)
796 */
797
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700798inline void handleGroupNameAttrPatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700799 const std::string& groupsAttribute,
800 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
801 const std::string& ldapServerElementName,
802 const std::string& ldapConfigObject)
803{
Ginu Georgee93abac2024-06-14 17:35:27 +0530804 setDbusProperty(asyncResp,
Ed Tanousd02aad32024-02-13 14:43:34 -0800805 ldapServerElementName +
806 "/LDAPService/SearchSettings/GroupsAttribute",
Ginu Georgee93abac2024-06-14 17:35:27 +0530807 ldapDbusService, ldapConfigObject, ldapConfigInterface,
808 "GroupNameAttribute", groupsAttribute);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700809}
810/**
811 * @brief updates the LDAP service enable and updates the
812 json response with the new value.
813 * @param input JSON data.
814 * @param asyncResp pointer to the JSON response
815 * @param ldapServerElementName Type of LDAP
816 server(openLDAP/ActiveDirectory)
817 */
818
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700819inline void handleServiceEnablePatch(
Ed Tanous6c51eab2021-06-03 12:30:29 -0700820 bool serviceEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
821 const std::string& ldapServerElementName,
822 const std::string& ldapConfigObject)
823{
Ginu Georgee93abac2024-06-14 17:35:27 +0530824 setDbusProperty(asyncResp, ldapServerElementName + "/ServiceEnabled",
825 ldapDbusService, ldapConfigObject, ldapEnableInterface,
826 "Enabled", serviceEnabled);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700827}
828
Ed Tanousc1019822024-03-06 12:54:38 -0800829struct AuthMethods
Ed Tanous6c51eab2021-06-03 12:30:29 -0700830{
831 std::optional<bool> basicAuth;
832 std::optional<bool> cookie;
833 std::optional<bool> sessionToken;
834 std::optional<bool> xToken;
835 std::optional<bool> tls;
Ed Tanousc1019822024-03-06 12:54:38 -0800836};
Ed Tanous6c51eab2021-06-03 12:30:29 -0700837
Ed Tanousc1019822024-03-06 12:54:38 -0800838inline void
839 handleAuthMethodsPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
840 const AuthMethods& auth)
841{
842 persistent_data::AuthConfigMethods& authMethodsConfig =
Ed Tanous6c51eab2021-06-03 12:30:29 -0700843 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
844
Ed Tanousc1019822024-03-06 12:54:38 -0800845 if (auth.basicAuth)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700846 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700847 if constexpr (!BMCWEB_BASIC_AUTH)
848 {
849 messages::actionNotSupported(
850 asyncResp->res,
851 "Setting BasicAuth when basic-auth feature is disabled");
852 return;
853 }
854
Ed Tanousc1019822024-03-06 12:54:38 -0800855 authMethodsConfig.basic = *auth.basicAuth;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700856 }
857
Ed Tanousc1019822024-03-06 12:54:38 -0800858 if (auth.cookie)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700859 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700860 if constexpr (!BMCWEB_COOKIE_AUTH)
861 {
862 messages::actionNotSupported(
863 asyncResp->res,
864 "Setting Cookie when cookie-auth feature is disabled");
865 return;
866 }
Ed Tanousc1019822024-03-06 12:54:38 -0800867 authMethodsConfig.cookie = *auth.cookie;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700868 }
869
Ed Tanousc1019822024-03-06 12:54:38 -0800870 if (auth.sessionToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700871 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700872 if constexpr (!BMCWEB_SESSION_AUTH)
873 {
874 messages::actionNotSupported(
875 asyncResp->res,
876 "Setting SessionToken when session-auth feature is disabled");
877 return;
878 }
Ed Tanousc1019822024-03-06 12:54:38 -0800879 authMethodsConfig.sessionToken = *auth.sessionToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700880 }
881
Ed Tanousc1019822024-03-06 12:54:38 -0800882 if (auth.xToken)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700883 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700884 if constexpr (!BMCWEB_XTOKEN_AUTH)
885 {
886 messages::actionNotSupported(
887 asyncResp->res,
888 "Setting XToken when xtoken-auth feature is disabled");
889 return;
890 }
Ed Tanousc1019822024-03-06 12:54:38 -0800891 authMethodsConfig.xtoken = *auth.xToken;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700892 }
893
Ed Tanousc1019822024-03-06 12:54:38 -0800894 if (auth.tls)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700895 {
Ed Tanous25b54db2024-04-17 15:40:31 -0700896 if constexpr (!BMCWEB_MUTUAL_TLS_AUTH)
897 {
898 messages::actionNotSupported(
899 asyncResp->res,
900 "Setting TLS when mutual-tls-auth feature is disabled");
901 return;
902 }
Ed Tanousc1019822024-03-06 12:54:38 -0800903 authMethodsConfig.tls = *auth.tls;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700904 }
905
906 if (!authMethodsConfig.basic && !authMethodsConfig.cookie &&
907 !authMethodsConfig.sessionToken && !authMethodsConfig.xtoken &&
908 !authMethodsConfig.tls)
909 {
910 // Do not allow user to disable everything
911 messages::actionNotSupported(asyncResp->res,
912 "of disabling all available methods");
913 return;
914 }
915
916 persistent_data::SessionStore::getInstance().updateAuthMethodsConfig(
917 authMethodsConfig);
918 // Save configuration immediately
919 persistent_data::getConfig().writeData();
920
921 messages::success(asyncResp->res);
922}
923
924/**
925 * @brief Get the required values from the given JSON, validates the
926 * value and create the LDAP config object.
927 * @param input JSON data
928 * @param asyncResp pointer to the JSON response
929 * @param serverType Type of LDAP server(openLDAP/ActiveDirectory)
930 */
931
Ed Tanous10cb44f2024-04-11 13:05:20 -0700932struct LdapPatchParams
933{
934 std::optional<std::string> authType;
935 std::optional<std::vector<std::string>> serviceAddressList;
936 std::optional<bool> serviceEnabled;
937 std::optional<std::vector<std::string>> baseDNList;
938 std::optional<std::string> userNameAttribute;
939 std::optional<std::string> groupsAttribute;
940 std::optional<std::string> userName;
941 std::optional<std::string> password;
942 std::optional<
943 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>
944 remoteRoleMapData;
945};
946
947inline void handleLDAPPatch(LdapPatchParams&& input,
Ed Tanous6c51eab2021-06-03 12:30:29 -0700948 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
949 const std::string& serverType)
950{
951 std::string dbusObjectPath;
952 if (serverType == "ActiveDirectory")
953 {
954 dbusObjectPath = adConfigObject;
955 }
956 else if (serverType == "LDAP")
957 {
958 dbusObjectPath = ldapConfigObjectName;
959 }
960 else
961 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700962 BMCWEB_LOG_ERROR("serverType wasn't AD or LDAP but was {}????",
963 serverType);
Ed Tanous6c51eab2021-06-03 12:30:29 -0700964 return;
965 }
966
Ed Tanous10cb44f2024-04-11 13:05:20 -0700967 if (input.authType && *input.authType != "UsernameAndPassword")
Ed Tanous6c51eab2021-06-03 12:30:29 -0700968 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700969 messages::propertyValueNotInList(asyncResp->res, *input.authType,
Ed Tanousc1019822024-03-06 12:54:38 -0800970 "AuthenticationType");
971 return;
Ed Tanous6c51eab2021-06-03 12:30:29 -0700972 }
Ed Tanousc1019822024-03-06 12:54:38 -0800973
Ed Tanous10cb44f2024-04-11 13:05:20 -0700974 if (input.serviceAddressList)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700975 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700976 if (input.serviceAddressList->empty())
Ratan Guptaeb2bbe52019-04-22 14:27:01 +0530977 {
Ed Tanouse2616cc2022-06-27 12:45:55 -0700978 messages::propertyValueNotInList(
Ed Tanous10cb44f2024-04-11 13:05:20 -0700979 asyncResp->res, *input.serviceAddressList, "ServiceAddress");
Ed Tanouscb13a392020-07-25 19:02:03 +0000980 return;
981 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700982 }
Ed Tanous10cb44f2024-04-11 13:05:20 -0700983 if (input.baseDNList)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700984 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700985 if (input.baseDNList->empty())
Ratan Gupta8a07d282019-03-16 08:33:47 +0530986 {
Ed Tanous10cb44f2024-04-11 13:05:20 -0700987 messages::propertyValueNotInList(asyncResp->res, *input.baseDNList,
Ed Tanous6c51eab2021-06-03 12:30:29 -0700988 "BaseDistinguishedNames");
Ratan Gupta8a07d282019-03-16 08:33:47 +0530989 return;
990 }
Ed Tanous6c51eab2021-06-03 12:30:29 -0700991 }
Ratan Gupta8a07d282019-03-16 08:33:47 +0530992
Ed Tanous6c51eab2021-06-03 12:30:29 -0700993 // nothing to update, then return
Ed Tanous10cb44f2024-04-11 13:05:20 -0700994 if (!input.userName && !input.password && !input.serviceAddressList &&
995 !input.baseDNList && !input.userNameAttribute &&
996 !input.groupsAttribute && !input.serviceEnabled &&
997 !input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -0700998 {
999 return;
1000 }
1001
1002 // Get the existing resource first then keep modifying
1003 // whenever any property gets updated.
Ed Tanous10cb44f2024-04-11 13:05:20 -07001004 getLDAPConfigData(serverType,
1005 [asyncResp, input = std::move(input),
1006 dbusObjectPath = std::move(dbusObjectPath)](
1007 bool success, const LDAPConfigData& confData,
1008 const std::string& serverT) mutable {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001009 if (!success)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301010 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001011 messages::internalError(asyncResp->res);
1012 return;
Ratan Gupta8a07d282019-03-16 08:33:47 +05301013 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001014 parseLDAPConfigData(asyncResp->res.jsonValue, confData, serverT);
1015 if (confData.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301016 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001017 // Disable the service first and update the rest of
1018 // the properties.
1019 handleServiceEnablePatch(false, asyncResp, serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301020 }
Ed Tanous6c51eab2021-06-03 12:30:29 -07001021
Ed Tanous10cb44f2024-04-11 13:05:20 -07001022 if (input.serviceAddressList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301023 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001024 handleServiceAddressPatch(*input.serviceAddressList, asyncResp,
1025 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301026 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001027 if (input.userName)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001028 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001029 handleUserNamePatch(*input.userName, asyncResp, serverT,
1030 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001031 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001032 if (input.password)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001033 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001034 handlePasswordPatch(*input.password, asyncResp, serverT,
1035 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001036 }
1037
Ed Tanous10cb44f2024-04-11 13:05:20 -07001038 if (input.baseDNList)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301039 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001040 handleBaseDNPatch(*input.baseDNList, asyncResp, serverT,
1041 dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001042 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001043 if (input.userNameAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001044 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001045 handleUserNameAttrPatch(*input.userNameAttribute, asyncResp,
1046 serverT, dbusObjectPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001047 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001048 if (input.groupsAttribute)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001049 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001050 handleGroupNameAttrPatch(*input.groupsAttribute, asyncResp, serverT,
Ed Tanous6c51eab2021-06-03 12:30:29 -07001051 dbusObjectPath);
1052 }
Ed Tanous10cb44f2024-04-11 13:05:20 -07001053 if (input.serviceEnabled)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001054 {
1055 // if user has given the value as true then enable
1056 // the service. if user has given false then no-op
1057 // as service is already stopped.
Ed Tanous10cb44f2024-04-11 13:05:20 -07001058 if (*input.serviceEnabled)
Ratan Gupta8a07d282019-03-16 08:33:47 +05301059 {
Ed Tanous10cb44f2024-04-11 13:05:20 -07001060 handleServiceEnablePatch(*input.serviceEnabled, asyncResp,
1061 serverT, dbusObjectPath);
Ratan Gupta8a07d282019-03-16 08:33:47 +05301062 }
1063 }
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001064 else
1065 {
Ed Tanous6c51eab2021-06-03 12:30:29 -07001066 // if user has not given the service enabled value
1067 // then revert it to the same state as it was
1068 // before.
1069 handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
1070 serverT, dbusObjectPath);
jayaprakash Mutyala96200602020-04-08 11:09:10 +00001071 }
Ed Tanous04ae99e2018-09-20 15:54:36 -07001072
Ed Tanous10cb44f2024-04-11 13:05:20 -07001073 if (input.remoteRoleMapData)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001074 {
1075 handleRoleMapPatch(asyncResp, confData.groupRoleList, serverT,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001076 *input.remoteRoleMapData);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001077 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001078 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001079}
1080
Abhishek Patel58345852022-02-02 08:54:25 -06001081inline void updateUserProperties(
1082 std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& username,
1083 const std::optional<std::string>& password,
1084 const std::optional<bool>& enabled,
1085 const std::optional<std::string>& roleId, const std::optional<bool>& locked,
Ravi Tejae518ef32024-05-16 10:33:08 -05001086 std::optional<std::vector<std::string>> accountTypes, bool userSelf,
1087 const std::shared_ptr<persistent_data::UserSession>& session)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001088{
P Dheeraj Srujan Kumarb477fd42021-12-16 07:17:51 +05301089 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1090 tempObjPath /= username;
1091 std::string dbusObjectPath(tempObjPath);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001092
1093 dbus::utility::checkDbusPathExists(
Ravi Tejae518ef32024-05-16 10:33:08 -05001094 dbusObjectPath,
1095 [dbusObjectPath, username, password, roleId, enabled, locked,
1096 accountTypes(std::move(accountTypes)), userSelf, session,
1097 asyncResp{std::move(asyncResp)}](int rc) {
Patrick Williams5a39f772023-10-20 11:20:21 -05001098 if (rc <= 0)
1099 {
1100 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1101 username);
1102 return;
1103 }
1104
1105 if (password)
1106 {
1107 int retval = pamUpdatePassword(username, *password);
1108
1109 if (retval == PAM_USER_UNKNOWN)
Ed Tanous6c51eab2021-06-03 12:30:29 -07001110 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001111 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1112 username);
Patrick Williams5a39f772023-10-20 11:20:21 -05001113 }
1114 else if (retval == PAM_AUTHTOK_ERR)
1115 {
1116 // If password is invalid
1117 messages::propertyValueFormatError(asyncResp->res, nullptr,
1118 "Password");
1119 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
1120 }
1121 else if (retval != PAM_SUCCESS)
1122 {
1123 messages::internalError(asyncResp->res);
Ed Tanous6c51eab2021-06-03 12:30:29 -07001124 return;
1125 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001126 else
Ed Tanous618c14b2022-06-30 17:44:25 -07001127 {
Ravi Tejae518ef32024-05-16 10:33:08 -05001128 // Remove existing sessions of the user when password changed
1129 persistent_data::SessionStore::getInstance()
1130 .removeSessionsByUsernameExceptSession(username, session);
Patrick Williams5a39f772023-10-20 11:20:21 -05001131 messages::success(asyncResp->res);
Ed Tanous618c14b2022-06-30 17:44:25 -07001132 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001133 }
Ed Tanous618c14b2022-06-30 17:44:25 -07001134
Patrick Williams5a39f772023-10-20 11:20:21 -05001135 if (enabled)
1136 {
Ginu Georgee93abac2024-06-14 17:35:27 +05301137 setDbusProperty(asyncResp, "Enabled",
1138 "xyz.openbmc_project.User.Manager", dbusObjectPath,
Ed Tanousd02aad32024-02-13 14:43:34 -08001139 "xyz.openbmc_project.User.Attributes",
Ginu Georgee93abac2024-06-14 17:35:27 +05301140 "UserEnabled", *enabled);
Patrick Williams5a39f772023-10-20 11:20:21 -05001141 }
1142
1143 if (roleId)
1144 {
1145 std::string priv = getPrivilegeFromRoleId(*roleId);
1146 if (priv.empty())
1147 {
1148 messages::propertyValueNotInList(asyncResp->res, true,
1149 "Locked");
1150 return;
Ed Tanous618c14b2022-06-30 17:44:25 -07001151 }
Ginu Georgee93abac2024-06-14 17:35:27 +05301152 setDbusProperty(asyncResp, "RoleId",
1153 "xyz.openbmc_project.User.Manager", dbusObjectPath,
Ed Tanousd02aad32024-02-13 14:43:34 -08001154 "xyz.openbmc_project.User.Attributes",
Ginu Georgee93abac2024-06-14 17:35:27 +05301155 "UserPrivilege", priv);
Patrick Williams5a39f772023-10-20 11:20:21 -05001156 }
1157
1158 if (locked)
1159 {
1160 // admin can unlock the account which is locked by
1161 // successive authentication failures but admin should
1162 // not be allowed to lock an account.
1163 if (*locked)
Abhishek Patel58345852022-02-02 08:54:25 -06001164 {
Patrick Williams5a39f772023-10-20 11:20:21 -05001165 messages::propertyValueNotInList(asyncResp->res, "true",
1166 "Locked");
1167 return;
Abhishek Patel58345852022-02-02 08:54:25 -06001168 }
Ginu Georgee93abac2024-06-14 17:35:27 +05301169 setDbusProperty(asyncResp, "Locked",
1170 "xyz.openbmc_project.User.Manager", dbusObjectPath,
Ed Tanousd02aad32024-02-13 14:43:34 -08001171 "xyz.openbmc_project.User.Attributes",
Ginu Georgee93abac2024-06-14 17:35:27 +05301172 "UserLockedForFailedAttempt", *locked);
Patrick Williams5a39f772023-10-20 11:20:21 -05001173 }
1174
1175 if (accountTypes)
1176 {
1177 patchAccountTypes(*accountTypes, asyncResp, dbusObjectPath,
1178 userSelf);
1179 }
1180 });
Ed Tanous6c51eab2021-06-03 12:30:29 -07001181}
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07001182
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001183inline void handleAccountServiceHead(
1184 App& app, const crow::Request& req,
1185 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001186{
1187 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1188 {
1189 return;
1190 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001191 asyncResp->res.addHeader(
1192 boost::beast::http::field::link,
1193 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1194}
1195
1196inline void
Ed Tanous1aa375b2024-04-13 11:51:10 -07001197 getClientCertificates(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1198 const nlohmann::json::json_pointer& keyLocation)
1199{
1200 boost::urls::url url(
1201 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates");
1202 std::array<std::string_view, 1> interfaces = {
1203 "xyz.openbmc_project.Certs.Certificate"};
1204 std::string path = "/xyz/openbmc_project/certs/authority/truststore";
1205
1206 collection_util::getCollectionToKey(asyncResp, url, interfaces, path,
1207 keyLocation);
1208}
1209
1210inline void handleAccountServiceClientCertificatesInstanceHead(
1211 App& app, const crow::Request& req,
1212 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1213 const std::string& /*id*/)
1214{
1215 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1216 {
1217 return;
1218 }
1219
1220 asyncResp->res.addHeader(
1221 boost::beast::http::field::link,
1222 "</redfish/v1/JsonSchemas/Certificate/Certificate.json>; rel=describedby");
1223}
1224
1225inline void handleAccountServiceClientCertificatesInstanceGet(
1226 App& app, const crow::Request& req,
1227 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1228{
1229 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1230 {
1231 return;
1232 }
1233 BMCWEB_LOG_DEBUG("ClientCertificate Certificate ID={}", id);
1234 const boost::urls::url certURL = boost::urls::format(
1235 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/{}",
1236 id);
1237 std::string objPath =
1238 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1239 getCertificateProperties(
1240 asyncResp, objPath,
1241 "xyz.openbmc_project.Certs.Manager.Authority.Truststore", id, certURL,
1242 "Client Certificate");
1243}
1244
1245inline void handleAccountServiceClientCertificatesHead(
1246 App& app, const crow::Request& req,
1247 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1248{
1249 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1250 {
1251 return;
1252 }
1253
1254 asyncResp->res.addHeader(
1255 boost::beast::http::field::link,
1256 "</redfish/v1/JsonSchemas/CertificateCollection/CertificateCollection.json>; rel=describedby");
1257}
1258
1259inline void handleAccountServiceClientCertificatesGet(
1260 App& app, const crow::Request& req,
1261 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1262{
1263 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1264 {
1265 return;
1266 }
1267 getClientCertificates(asyncResp, "/Members"_json_pointer);
1268}
1269
Ed Tanous3ce36882024-06-09 10:58:16 -07001270using account_service::CertificateMappingAttribute;
1271using persistent_data::MTLSCommonNameParseMode;
1272inline CertificateMappingAttribute
1273 getCertificateMapping(MTLSCommonNameParseMode parse)
1274{
1275 switch (parse)
1276 {
1277 case MTLSCommonNameParseMode::CommonName:
1278 {
1279 return CertificateMappingAttribute::CommonName;
1280 }
1281 break;
1282 case MTLSCommonNameParseMode::Whole:
1283 {
1284 return CertificateMappingAttribute::Whole;
1285 }
1286 break;
1287 case MTLSCommonNameParseMode::UserPrincipalName:
1288 {
1289 return CertificateMappingAttribute::UserPrincipalName;
1290 }
1291 break;
1292
1293 case MTLSCommonNameParseMode::Meta:
1294 {
1295 if constexpr (BMCWEB_META_TLS_COMMON_NAME_PARSING)
1296 {
1297 return CertificateMappingAttribute::CommonName;
1298 }
1299 }
1300 break;
1301 default:
1302 {
1303 return CertificateMappingAttribute::Invalid;
1304 }
1305 break;
1306 }
1307}
1308
Ed Tanous1aa375b2024-04-13 11:51:10 -07001309inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001310 handleAccountServiceGet(App& app, const crow::Request& req,
1311 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1312{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001313 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1314 {
1315 return;
1316 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001317
1318 if (req.session == nullptr)
1319 {
1320 messages::internalError(asyncResp->res);
1321 return;
1322 }
1323
Ed Tanousc1019822024-03-06 12:54:38 -08001324 const persistent_data::AuthConfigMethods& authMethodsConfig =
1325 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1326
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001327 asyncResp->res.addHeader(
1328 boost::beast::http::field::link,
1329 "</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
1330
Ed Tanous1ef4c342022-05-12 16:12:36 -07001331 nlohmann::json& json = asyncResp->res.jsonValue;
1332 json["@odata.id"] = "/redfish/v1/AccountService";
Ravi Teja482a69e2024-04-22 06:56:13 -05001333 json["@odata.type"] = "#AccountService.v1_15_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001334 json["Id"] = "AccountService";
1335 json["Name"] = "Account Service";
1336 json["Description"] = "Account Service";
1337 json["ServiceEnabled"] = true;
1338 json["MaxPasswordLength"] = 20;
1339 json["Accounts"]["@odata.id"] = "/redfish/v1/AccountService/Accounts";
1340 json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
Ravi Teja482a69e2024-04-22 06:56:13 -05001341 json["HTTPBasicAuth"] = authMethodsConfig.basic
1342 ? account_service::BasicAuthState::Enabled
1343 : account_service::BasicAuthState::Disabled;
1344
1345 nlohmann::json::array_t allowed;
1346 allowed.emplace_back(account_service::BasicAuthState::Enabled);
1347 allowed.emplace_back(account_service::BasicAuthState::Disabled);
1348 json["HTTPBasicAuth@AllowableValues"] = std::move(allowed);
1349
Ed Tanous1aa375b2024-04-13 11:51:10 -07001350 nlohmann::json::object_t clientCertificate;
1351 clientCertificate["Enabled"] = authMethodsConfig.tls;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001352 clientCertificate["RespondToUnauthenticatedClients"] =
1353 !authMethodsConfig.tlsStrict;
Ed Tanous3ce36882024-06-09 10:58:16 -07001354
1355 using account_service::CertificateMappingAttribute;
1356
1357 CertificateMappingAttribute mapping =
1358 getCertificateMapping(authMethodsConfig.mTLSCommonNameParsingMode);
1359 if (mapping == CertificateMappingAttribute::Invalid)
1360 {
1361 messages::internalError(asyncResp->res);
1362 }
1363 else
1364 {
1365 clientCertificate["CertificateMappingAttribute"] = mapping;
1366 }
Ed Tanous1aa375b2024-04-13 11:51:10 -07001367 nlohmann::json::object_t certificates;
1368 certificates["@odata.id"] =
1369 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates";
1370 certificates["@odata.type"] =
1371 "#CertificateCollection.CertificateCollection";
1372 clientCertificate["Certificates"] = std::move(certificates);
1373 json["MultiFactorAuth"]["ClientCertificate"] = std::move(clientCertificate);
1374
1375 getClientCertificates(
1376 asyncResp,
1377 "/MultiFactorAuth/ClientCertificate/Certificates/Members"_json_pointer);
1378
Ed Tanous1ef4c342022-05-12 16:12:36 -07001379 json["Oem"]["OpenBMC"]["@odata.type"] =
Ed Tanous5b5574a2022-09-26 19:53:36 -07001380 "#OpenBMCAccountService.v1_0_0.AccountService";
Ed Tanous1ef4c342022-05-12 16:12:36 -07001381 json["Oem"]["OpenBMC"]["@odata.id"] =
1382 "/redfish/v1/AccountService#/Oem/OpenBMC";
1383 json["Oem"]["OpenBMC"]["AuthMethods"]["BasicAuth"] =
1384 authMethodsConfig.basic;
1385 json["Oem"]["OpenBMC"]["AuthMethods"]["SessionToken"] =
1386 authMethodsConfig.sessionToken;
1387 json["Oem"]["OpenBMC"]["AuthMethods"]["XToken"] = authMethodsConfig.xtoken;
1388 json["Oem"]["OpenBMC"]["AuthMethods"]["Cookie"] = authMethodsConfig.cookie;
1389 json["Oem"]["OpenBMC"]["AuthMethods"]["TLS"] = authMethodsConfig.tls;
1390
1391 // /redfish/v1/AccountService/LDAP/Certificates is something only
1392 // ConfigureManager can access then only display when the user has
1393 // permissions ConfigureManager
1394 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001395 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001396
1397 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
1398 effectiveUserPrivileges))
1399 {
1400 asyncResp->res.jsonValue["LDAP"]["Certificates"]["@odata.id"] =
1401 "/redfish/v1/AccountService/LDAP/Certificates";
1402 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001403 sdbusplus::asio::getAllProperties(
1404 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1405 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.AccountPolicy",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001406 [asyncResp](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001407 const dbus::utility::DBusPropertiesMap& propertiesList) {
1408 if (ec)
1409 {
1410 messages::internalError(asyncResp->res);
1411 return;
1412 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001413
Ed Tanous1aa375b2024-04-13 11:51:10 -07001414 BMCWEB_LOG_DEBUG("Got {} properties for AccountService",
Ed Tanous62598e32023-07-17 17:06:25 -07001415 propertiesList.size());
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001416
1417 const uint8_t* minPasswordLength = nullptr;
1418 const uint32_t* accountUnlockTimeout = nullptr;
1419 const uint16_t* maxLoginAttemptBeforeLockout = nullptr;
1420
1421 const bool success = sdbusplus::unpackPropertiesNoThrow(
1422 dbus_utils::UnpackErrorPrinter(), propertiesList,
1423 "MinPasswordLength", minPasswordLength, "AccountUnlockTimeout",
1424 accountUnlockTimeout, "MaxLoginAttemptBeforeLockout",
1425 maxLoginAttemptBeforeLockout);
1426
1427 if (!success)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001428 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001429 messages::internalError(asyncResp->res);
1430 return;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001431 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001432
1433 if (minPasswordLength != nullptr)
1434 {
1435 asyncResp->res.jsonValue["MinPasswordLength"] = *minPasswordLength;
1436 }
1437
1438 if (accountUnlockTimeout != nullptr)
1439 {
1440 asyncResp->res.jsonValue["AccountLockoutDuration"] =
1441 *accountUnlockTimeout;
1442 }
1443
1444 if (maxLoginAttemptBeforeLockout != nullptr)
1445 {
1446 asyncResp->res.jsonValue["AccountLockoutThreshold"] =
1447 *maxLoginAttemptBeforeLockout;
1448 }
Patrick Williams5a39f772023-10-20 11:20:21 -05001449 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001450
Ed Tanous02cad962022-06-30 16:50:15 -07001451 auto callback = [asyncResp](bool success, const LDAPConfigData& confData,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001452 const std::string& ldapType) {
1453 if (!success)
1454 {
1455 return;
1456 }
1457 parseLDAPConfigData(asyncResp->res.jsonValue, confData, ldapType);
1458 };
1459
1460 getLDAPConfigData("LDAP", callback);
1461 getLDAPConfigData("ActiveDirectory", callback);
1462}
1463
Ed Tanous3ce36882024-06-09 10:58:16 -07001464inline void
1465 handleCertificateMappingAttributePatch(crow::Response& res,
1466 const std::string& certMapAttribute)
1467{
1468 MTLSCommonNameParseMode parseMode =
1469 persistent_data::getMTLSCommonNameParseMode(certMapAttribute);
1470 if (parseMode == MTLSCommonNameParseMode::Invalid)
1471 {
1472 messages::propertyValueNotInList(res, "CertificateMappingAttribute",
1473 certMapAttribute);
1474 return;
1475 }
1476
1477 persistent_data::AuthConfigMethods& authMethodsConfig =
1478 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1479 authMethodsConfig.mTLSCommonNameParsingMode = parseMode;
1480}
1481
Ed Tanous3281bcf2024-06-25 16:02:05 -07001482inline void handleRespondToUnauthenticatedClientsPatch(
1483 App& app, const crow::Request& req, crow::Response& res,
1484 bool respondToUnauthenticatedClients)
1485{
1486 if (req.session != nullptr)
1487 {
1488 // Sanity check. If the user isn't currently authenticated with mutual
1489 // TLS, they very likely are about to permanently lock themselves out.
1490 // Make sure they're using mutual TLS before allowing locking.
1491 if (req.session->sessionType != persistent_data::SessionType::MutualTLS)
1492 {
1493 messages::propertyValueExternalConflict(
1494 res,
1495 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients",
1496 respondToUnauthenticatedClients);
1497 return;
1498 }
1499 }
1500
1501 persistent_data::AuthConfigMethods& authMethodsConfig =
1502 persistent_data::SessionStore::getInstance().getAuthMethodsConfig();
1503
1504 // Change the settings
1505 authMethodsConfig.tlsStrict = !respondToUnauthenticatedClients;
1506
1507 // Write settings to disk
1508 persistent_data::getConfig().writeData();
1509
1510 // Trigger a reload, to apply the new settings to new connections
1511 app.loadCertificate();
1512}
1513
Ed Tanous1ef4c342022-05-12 16:12:36 -07001514inline void handleAccountServicePatch(
1515 App& app, const crow::Request& req,
1516 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1517{
1518 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1519 {
1520 return;
1521 }
1522 std::optional<uint32_t> unlockTimeout;
1523 std::optional<uint16_t> lockoutThreshold;
1524 std::optional<uint8_t> minPasswordLength;
1525 std::optional<uint16_t> maxPasswordLength;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001526 LdapPatchParams ldapObject;
Ed Tanous3ce36882024-06-09 10:58:16 -07001527 std::optional<std::string> certificateMappingAttribute;
Ed Tanous3281bcf2024-06-25 16:02:05 -07001528 std::optional<bool> respondToUnauthenticatedClients;
Ed Tanous10cb44f2024-04-11 13:05:20 -07001529 LdapPatchParams activeDirectoryObject;
Ed Tanousc1019822024-03-06 12:54:38 -08001530 AuthMethods auth;
Ravi Teja482a69e2024-04-22 06:56:13 -05001531 std::optional<std::string> httpBasicAuth;
Ed Tanous3ce36882024-06-09 10:58:16 -07001532
Ed Tanousc1019822024-03-06 12:54:38 -08001533 // clang-format off
Ed Tanous1ef4c342022-05-12 16:12:36 -07001534 if (!json_util::readJsonPatch(
Ed Tanousc1019822024-03-06 12:54:38 -08001535 req, asyncResp->res,
1536 "AccountLockoutDuration", unlockTimeout,
1537 "AccountLockoutThreshold", lockoutThreshold,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001538 "ActiveDirectory/Authentication/AuthenticationType", activeDirectoryObject.authType,
1539 "ActiveDirectory/Authentication/Password", activeDirectoryObject.password,
1540 "ActiveDirectory/Authentication/Username", activeDirectoryObject.userName,
1541 "ActiveDirectory/LDAPService/SearchSettings/BaseDistinguishedNames", activeDirectoryObject.baseDNList,
1542 "ActiveDirectory/LDAPService/SearchSettings/GroupsAttribute", activeDirectoryObject.groupsAttribute,
1543 "ActiveDirectory/LDAPService/SearchSettings/UsernameAttribute", activeDirectoryObject.userNameAttribute,
1544 "ActiveDirectory/RemoteRoleMapping", activeDirectoryObject.remoteRoleMapData,
1545 "ActiveDirectory/ServiceAddresses", activeDirectoryObject.serviceAddressList,
1546 "ActiveDirectory/ServiceEnabled", activeDirectoryObject.serviceEnabled,
Ed Tanous3ce36882024-06-09 10:58:16 -07001547 "MultiFactorAuth/ClientCertificate/CertificateMappingAttribute", certificateMappingAttribute,
Ed Tanous3281bcf2024-06-25 16:02:05 -07001548 "MultiFactorAuth/ClientCertificate/RespondToUnauthenticatedClients", respondToUnauthenticatedClients,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001549 "LDAP/Authentication/AuthenticationType", ldapObject.authType,
1550 "LDAP/Authentication/Password", ldapObject.password,
1551 "LDAP/Authentication/Username", ldapObject.userName,
1552 "LDAP/LDAPService/SearchSettings/BaseDistinguishedNames", ldapObject.baseDNList,
1553 "LDAP/LDAPService/SearchSettings/GroupsAttribute", ldapObject.groupsAttribute,
1554 "LDAP/LDAPService/SearchSettings/UsernameAttribute", ldapObject.userNameAttribute,
1555 "LDAP/RemoteRoleMapping", ldapObject.remoteRoleMapData,
1556 "LDAP/ServiceAddresses", ldapObject.serviceAddressList,
1557 "LDAP/ServiceEnabled", ldapObject.serviceEnabled,
Ed Tanousc1019822024-03-06 12:54:38 -08001558 "MaxPasswordLength", maxPasswordLength,
1559 "MinPasswordLength", minPasswordLength,
Ed Tanousc1019822024-03-06 12:54:38 -08001560 "Oem/OpenBMC/AuthMethods/BasicAuth", auth.basicAuth,
1561 "Oem/OpenBMC/AuthMethods/Cookie", auth.cookie,
1562 "Oem/OpenBMC/AuthMethods/SessionToken", auth.sessionToken,
Ed Tanous10cb44f2024-04-11 13:05:20 -07001563 "Oem/OpenBMC/AuthMethods/TLS", auth.tls,
Ravi Teja482a69e2024-04-22 06:56:13 -05001564 "Oem/OpenBMC/AuthMethods/XToken", auth.xToken,
1565 "HTTPBasicAuth", httpBasicAuth))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001566 {
1567 return;
1568 }
Ed Tanousc1019822024-03-06 12:54:38 -08001569 // clang-format on
Ed Tanous1ef4c342022-05-12 16:12:36 -07001570
Ravi Teja482a69e2024-04-22 06:56:13 -05001571 if (httpBasicAuth)
1572 {
1573 if (*httpBasicAuth == "Enabled")
1574 {
1575 auth.basicAuth = true;
1576 }
1577 else if (*httpBasicAuth == "Disabled")
1578 {
1579 auth.basicAuth = false;
1580 }
1581 else
1582 {
1583 messages::propertyValueNotInList(asyncResp->res, "HttpBasicAuth",
1584 *httpBasicAuth);
1585 }
1586 }
1587
Ed Tanous3281bcf2024-06-25 16:02:05 -07001588 if (respondToUnauthenticatedClients)
1589 {
1590 handleRespondToUnauthenticatedClientsPatch(
1591 app, req, asyncResp->res, *respondToUnauthenticatedClients);
1592 }
1593
Ed Tanous3ce36882024-06-09 10:58:16 -07001594 if (certificateMappingAttribute)
1595 {
1596 handleCertificateMappingAttributePatch(asyncResp->res,
1597 *certificateMappingAttribute);
1598 }
1599
Ed Tanous1ef4c342022-05-12 16:12:36 -07001600 if (minPasswordLength)
1601 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001602 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301603 asyncResp, "MinPasswordLength", "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001604 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001605 "xyz.openbmc_project.User.AccountPolicy", "MinPasswordLength",
Ginu Georgee93abac2024-06-14 17:35:27 +05301606 *minPasswordLength);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001607 }
1608
1609 if (maxPasswordLength)
1610 {
1611 messages::propertyNotWritable(asyncResp->res, "MaxPasswordLength");
1612 }
1613
Ed Tanous10cb44f2024-04-11 13:05:20 -07001614 handleLDAPPatch(std::move(activeDirectoryObject), asyncResp,
1615 "ActiveDirectory");
1616 handleLDAPPatch(std::move(ldapObject), asyncResp, "LDAP");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001617
Ed Tanousc1019822024-03-06 12:54:38 -08001618 handleAuthMethodsPatch(asyncResp, auth);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001619
Ed Tanous1ef4c342022-05-12 16:12:36 -07001620 if (unlockTimeout)
1621 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001622 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301623 asyncResp, "AccountLockoutDuration",
1624 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001625 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
Ed Tanous1ef4c342022-05-12 16:12:36 -07001626 "xyz.openbmc_project.User.AccountPolicy", "AccountUnlockTimeout",
Ginu Georgee93abac2024-06-14 17:35:27 +05301627 *unlockTimeout);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001628 }
1629 if (lockoutThreshold)
1630 {
Ed Tanousd02aad32024-02-13 14:43:34 -08001631 setDbusProperty(
Ginu Georgee93abac2024-06-14 17:35:27 +05301632 asyncResp, "AccountLockoutThreshold",
1633 "xyz.openbmc_project.User.Manager",
Ed Tanousd02aad32024-02-13 14:43:34 -08001634 sdbusplus::message::object_path("/xyz/openbmc_project/user"),
George Liu9ae226f2023-06-21 17:56:46 +08001635 "xyz.openbmc_project.User.AccountPolicy",
Ginu Georgee93abac2024-06-14 17:35:27 +05301636 "MaxLoginAttemptBeforeLockout", *lockoutThreshold);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001637 }
1638}
1639
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001640inline void handleAccountCollectionHead(
Ed Tanous1ef4c342022-05-12 16:12:36 -07001641 App& app, const crow::Request& req,
1642 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1643{
1644 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1645 {
1646 return;
1647 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001648 asyncResp->res.addHeader(
1649 boost::beast::http::field::link,
1650 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
1651}
1652
1653inline void handleAccountCollectionGet(
1654 App& app, const crow::Request& req,
1655 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1656{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001657 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1658 {
1659 return;
1660 }
Ninad Palsule3e72c202023-03-27 17:19:55 -05001661
1662 if (req.session == nullptr)
1663 {
1664 messages::internalError(asyncResp->res);
1665 return;
1666 }
1667
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001668 asyncResp->res.addHeader(
1669 boost::beast::http::field::link,
1670 "</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001671
1672 asyncResp->res.jsonValue["@odata.id"] =
1673 "/redfish/v1/AccountService/Accounts";
1674 asyncResp->res.jsonValue["@odata.type"] = "#ManagerAccountCollection."
1675 "ManagerAccountCollection";
1676 asyncResp->res.jsonValue["Name"] = "Accounts Collection";
1677 asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
1678
1679 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001680 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001681
1682 std::string thisUser;
1683 if (req.session)
1684 {
1685 thisUser = req.session->username;
1686 }
George Liu5eb468d2023-06-20 17:03:24 +08001687 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
1688 dbus::utility::getManagedObjects(
1689 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001690 [asyncResp, thisUser, effectiveUserPrivileges](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001691 const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001692 const dbus::utility::ManagedObjectType& users) {
1693 if (ec)
1694 {
1695 messages::internalError(asyncResp->res);
1696 return;
1697 }
1698
1699 bool userCanSeeAllAccounts =
1700 effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"});
1701
1702 bool userCanSeeSelf =
1703 effectiveUserPrivileges.isSupersetOf({"ConfigureSelf"});
1704
1705 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
1706 memberArray = nlohmann::json::array();
1707
1708 for (const auto& userpath : users)
1709 {
1710 std::string user = userpath.first.filename();
1711 if (user.empty())
1712 {
1713 messages::internalError(asyncResp->res);
Ed Tanous62598e32023-07-17 17:06:25 -07001714 BMCWEB_LOG_ERROR("Invalid firmware ID");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001715
1716 return;
1717 }
1718
1719 // As clarified by Redfish here:
1720 // https://redfishforum.com/thread/281/manageraccountcollection-change-allows-account-enumeration
1721 // Users without ConfigureUsers, only see their own
1722 // account. Users with ConfigureUsers, see all
1723 // accounts.
1724 if (userCanSeeAllAccounts || (thisUser == user && userCanSeeSelf))
1725 {
1726 nlohmann::json::object_t member;
Ed Tanous3b327802023-08-14 09:23:43 -07001727 member["@odata.id"] = boost::urls::format(
1728 "/redfish/v1/AccountService/Accounts/{}", user);
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001729 memberArray.emplace_back(std::move(member));
Ed Tanous1ef4c342022-05-12 16:12:36 -07001730 }
1731 }
1732 asyncResp->res.jsonValue["Members@odata.count"] = memberArray.size();
Patrick Williams5a39f772023-10-20 11:20:21 -05001733 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001734}
1735
Ninad Palsule97e90da2023-05-17 14:04:52 -05001736inline void processAfterCreateUser(
1737 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1738 const std::string& username, const std::string& password,
1739 const boost::system::error_code& ec, sdbusplus::message_t& m)
1740{
1741 if (ec)
1742 {
1743 userErrorMessageHandler(m.get_error(), asyncResp, username, "");
1744 return;
1745 }
1746
1747 if (pamUpdatePassword(username, password) != PAM_SUCCESS)
1748 {
1749 // At this point we have a user that's been
1750 // created, but the password set
1751 // failed.Something is wrong, so delete the user
1752 // that we've already created
1753 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
1754 tempObjPath /= username;
1755 const std::string userPath(tempObjPath);
1756
1757 crow::connections::systemBus->async_method_call(
1758 [asyncResp, password](const boost::system::error_code& ec3) {
1759 if (ec3)
1760 {
1761 messages::internalError(asyncResp->res);
1762 return;
1763 }
1764
1765 // If password is invalid
Jason M. Bills9bd80832023-08-30 15:19:41 -07001766 messages::propertyValueFormatError(asyncResp->res, nullptr,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001767 "Password");
Patrick Williams5a39f772023-10-20 11:20:21 -05001768 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001769 "xyz.openbmc_project.User.Manager", userPath,
1770 "xyz.openbmc_project.Object.Delete", "Delete");
1771
Ed Tanous62598e32023-07-17 17:06:25 -07001772 BMCWEB_LOG_ERROR("pamUpdatePassword Failed");
Ninad Palsule97e90da2023-05-17 14:04:52 -05001773 return;
1774 }
1775
1776 messages::created(asyncResp->res);
1777 asyncResp->res.addHeader("Location",
1778 "/redfish/v1/AccountService/Accounts/" + username);
1779}
1780
1781inline void processAfterGetAllGroups(
1782 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1783 const std::string& username, const std::string& password,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001784 const std::string& roleId, bool enabled,
Ninad Palsule9ba73932023-06-01 16:38:57 -05001785 std::optional<std::vector<std::string>> accountTypes,
Ninad Palsule97e90da2023-05-17 14:04:52 -05001786 const std::vector<std::string>& allGroupsList)
Ninad Palsule97e90da2023-05-17 14:04:52 -05001787{
Ninad Palsule3e72c202023-03-27 17:19:55 -05001788 std::vector<std::string> userGroups;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001789 std::vector<std::string> accountTypeUserGroups;
1790
1791 // If user specified account types then convert them to unix user groups
1792 if (accountTypes)
1793 {
1794 if (!getUserGroupFromAccountType(asyncResp->res, *accountTypes,
1795 accountTypeUserGroups))
1796 {
1797 // Problem in mapping Account Types to User Groups, Error already
1798 // logged.
1799 return;
1800 }
1801 }
1802
Ninad Palsule3e72c202023-03-27 17:19:55 -05001803 for (const auto& grp : allGroupsList)
1804 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001805 // If user specified the account type then only accept groups which are
1806 // in the account types group list.
1807 if (!accountTypeUserGroups.empty())
1808 {
1809 bool found = false;
1810 for (const auto& grp1 : accountTypeUserGroups)
1811 {
1812 if (grp == grp1)
1813 {
1814 found = true;
1815 break;
1816 }
1817 }
1818 if (!found)
1819 {
1820 continue;
1821 }
1822 }
1823
Ninad Palsule3e72c202023-03-27 17:19:55 -05001824 // Console access is provided to the user who is a member of
1825 // hostconsole group and has a administrator role. So, set
1826 // hostconsole group only for the administrator.
Ninad Palsule9ba73932023-06-01 16:38:57 -05001827 if ((grp == "hostconsole") && (roleId != "priv-admin"))
Ninad Palsule3e72c202023-03-27 17:19:55 -05001828 {
Ninad Palsule9ba73932023-06-01 16:38:57 -05001829 if (!accountTypeUserGroups.empty())
1830 {
Ed Tanous62598e32023-07-17 17:06:25 -07001831 BMCWEB_LOG_ERROR(
1832 "Only administrator can get HostConsole access");
Ninad Palsule9ba73932023-06-01 16:38:57 -05001833 asyncResp->res.result(boost::beast::http::status::bad_request);
1834 return;
1835 }
1836 continue;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001837 }
Ninad Palsule9ba73932023-06-01 16:38:57 -05001838 userGroups.emplace_back(grp);
1839 }
1840
1841 // Make sure user specified groups are valid. This is internal error because
1842 // it some inconsistencies between user manager and bmcweb.
1843 if (!accountTypeUserGroups.empty() &&
1844 accountTypeUserGroups.size() != userGroups.size())
1845 {
1846 messages::internalError(asyncResp->res);
1847 return;
Ninad Palsule3e72c202023-03-27 17:19:55 -05001848 }
Ninad Palsule97e90da2023-05-17 14:04:52 -05001849 crow::connections::systemBus->async_method_call(
1850 [asyncResp, username, password](const boost::system::error_code& ec2,
1851 sdbusplus::message_t& m) {
1852 processAfterCreateUser(asyncResp, username, password, ec2, m);
Patrick Williams5a39f772023-10-20 11:20:21 -05001853 },
Ninad Palsule97e90da2023-05-17 14:04:52 -05001854 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ninad Palsule3e72c202023-03-27 17:19:55 -05001855 "xyz.openbmc_project.User.Manager", "CreateUser", username, userGroups,
Ed Tanouse01d0c32023-06-30 13:21:32 -07001856 roleId, enabled);
Ninad Palsule97e90da2023-05-17 14:04:52 -05001857}
1858
Ed Tanous1ef4c342022-05-12 16:12:36 -07001859inline void handleAccountCollectionPost(
1860 App& app, const crow::Request& req,
1861 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1862{
1863 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1864 {
1865 return;
1866 }
1867 std::string username;
1868 std::string password;
Ed Tanouse01d0c32023-06-30 13:21:32 -07001869 std::optional<std::string> roleIdJson;
1870 std::optional<bool> enabledJson;
Ninad Palsule9ba73932023-06-01 16:38:57 -05001871 std::optional<std::vector<std::string>> accountTypes;
Ed Tanouse01d0c32023-06-30 13:21:32 -07001872 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", username,
1873 "Password", password, "RoleId", roleIdJson,
1874 "Enabled", enabledJson, "AccountTypes",
1875 accountTypes))
Ed Tanous1ef4c342022-05-12 16:12:36 -07001876 {
1877 return;
1878 }
1879
Ed Tanouse01d0c32023-06-30 13:21:32 -07001880 std::string roleId = roleIdJson.value_or("User");
1881 std::string priv = getPrivilegeFromRoleId(roleId);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001882 if (priv.empty())
1883 {
Ed Tanouse01d0c32023-06-30 13:21:32 -07001884 messages::propertyValueNotInList(asyncResp->res, roleId, "RoleId");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001885 return;
1886 }
Asmitha Karunanithi239adf82022-03-25 02:59:03 -05001887 roleId = priv;
Ed Tanous1ef4c342022-05-12 16:12:36 -07001888
Ed Tanouse01d0c32023-06-30 13:21:32 -07001889 bool enabled = enabledJson.value_or(true);
1890
Ed Tanous1ef4c342022-05-12 16:12:36 -07001891 // Reading AllGroups property
1892 sdbusplus::asio::getProperty<std::vector<std::string>>(
1893 *crow::connections::systemBus, "xyz.openbmc_project.User.Manager",
1894 "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager",
1895 "AllGroups",
Ninad Palsule9ba73932023-06-01 16:38:57 -05001896 [asyncResp, username, password{std::move(password)}, roleId, enabled,
1897 accountTypes](const boost::system::error_code& ec,
1898 const std::vector<std::string>& allGroupsList) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07001899 if (ec)
1900 {
Ed Tanous62598e32023-07-17 17:06:25 -07001901 BMCWEB_LOG_DEBUG("ERROR with async_method_call");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001902 messages::internalError(asyncResp->res);
1903 return;
1904 }
1905
1906 if (allGroupsList.empty())
1907 {
1908 messages::internalError(asyncResp->res);
1909 return;
1910 }
1911
Ninad Palsule97e90da2023-05-17 14:04:52 -05001912 processAfterGetAllGroups(asyncResp, username, password, roleId, enabled,
Ninad Palsule9ba73932023-06-01 16:38:57 -05001913 accountTypes, allGroupsList);
Patrick Williams5a39f772023-10-20 11:20:21 -05001914 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001915}
1916
1917inline void
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001918 handleAccountHead(App& app, const crow::Request& req,
1919 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1920 const std::string& /*accountName*/)
Ed Tanous1ef4c342022-05-12 16:12:36 -07001921{
1922 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1923 {
1924 return;
1925 }
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001926 asyncResp->res.addHeader(
1927 boost::beast::http::field::link,
1928 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1929}
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001930
Ed Tanous4c7d4d32022-07-07 15:29:35 -07001931inline void
1932 handleAccountGet(App& app, const crow::Request& req,
1933 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1934 const std::string& accountName)
1935{
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001936 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1937 {
1938 return;
1939 }
1940 asyncResp->res.addHeader(
1941 boost::beast::http::field::link,
1942 "</redfish/v1/JsonSchemas/ManagerAccount/ManagerAccount.json>; rel=describedby");
1943
Ed Tanous25b54db2024-04-17 15:40:31 -07001944 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
1945 {
1946 // If authentication is disabled, there are no user accounts
1947 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1948 accountName);
1949 return;
1950 }
Jiaqing Zhaoafd369c2023-03-07 15:12:22 +08001951
Ed Tanous1ef4c342022-05-12 16:12:36 -07001952 if (req.session == nullptr)
1953 {
1954 messages::internalError(asyncResp->res);
1955 return;
1956 }
1957 if (req.session->username != accountName)
1958 {
1959 // At this point we've determined that the user is trying to
1960 // modify a user that isn't them. We need to verify that they
1961 // have permissions to modify other users, so re-run the auth
1962 // check with the same permissions, minus ConfigureSelf.
1963 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05001964 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07001965 Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
1966 "ConfigureManager"};
1967 if (!effectiveUserPrivileges.isSupersetOf(
1968 requiredPermissionsToChangeNonSelf))
1969 {
Ed Tanous62598e32023-07-17 17:06:25 -07001970 BMCWEB_LOG_DEBUG("GET Account denied access");
Ed Tanous1ef4c342022-05-12 16:12:36 -07001971 messages::insufficientPrivilege(asyncResp->res);
1972 return;
1973 }
1974 }
1975
George Liu5eb468d2023-06-20 17:03:24 +08001976 sdbusplus::message::object_path path("/xyz/openbmc_project/user");
1977 dbus::utility::getManagedObjects(
1978 "xyz.openbmc_project.User.Manager", path,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001979 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001980 accountName](const boost::system::error_code& ec,
Ed Tanous1ef4c342022-05-12 16:12:36 -07001981 const dbus::utility::ManagedObjectType& users) {
1982 if (ec)
1983 {
1984 messages::internalError(asyncResp->res);
1985 return;
1986 }
Ed Tanous3544d2a2023-08-06 18:12:20 -07001987 const auto userIt = std::ranges::find_if(
Michael Shen80f79a42023-08-24 13:41:53 +00001988 users,
1989 [accountName](
1990 const std::pair<sdbusplus::message::object_path,
1991 dbus::utility::DBusInterfacesMap>& user) {
1992 return accountName == user.first.filename();
Patrick Williams5a39f772023-10-20 11:20:21 -05001993 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07001994
1995 if (userIt == users.end())
1996 {
1997 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
1998 accountName);
1999 return;
2000 }
2001
2002 asyncResp->res.jsonValue["@odata.type"] =
Abhishek Patel58345852022-02-02 08:54:25 -06002003 "#ManagerAccount.v1_7_0.ManagerAccount";
Ed Tanous1ef4c342022-05-12 16:12:36 -07002004 asyncResp->res.jsonValue["Name"] = "User Account";
2005 asyncResp->res.jsonValue["Description"] = "User Account";
2006 asyncResp->res.jsonValue["Password"] = nullptr;
Abhishek Patel58345852022-02-02 08:54:25 -06002007 asyncResp->res.jsonValue["StrictAccountTypes"] = true;
Ed Tanous1ef4c342022-05-12 16:12:36 -07002008
2009 for (const auto& interface : userIt->second)
2010 {
2011 if (interface.first == "xyz.openbmc_project.User.Attributes")
2012 {
2013 for (const auto& property : interface.second)
2014 {
2015 if (property.first == "UserEnabled")
2016 {
2017 const bool* userEnabled =
2018 std::get_if<bool>(&property.second);
2019 if (userEnabled == nullptr)
2020 {
Ed Tanous62598e32023-07-17 17:06:25 -07002021 BMCWEB_LOG_ERROR("UserEnabled wasn't a bool");
Ed Tanous1ef4c342022-05-12 16:12:36 -07002022 messages::internalError(asyncResp->res);
2023 return;
2024 }
2025 asyncResp->res.jsonValue["Enabled"] = *userEnabled;
2026 }
2027 else if (property.first == "UserLockedForFailedAttempt")
2028 {
2029 const bool* userLocked =
2030 std::get_if<bool>(&property.second);
2031 if (userLocked == nullptr)
2032 {
Ed Tanous62598e32023-07-17 17:06:25 -07002033 BMCWEB_LOG_ERROR("UserLockedForF"
2034 "ailedAttempt "
2035 "wasn't a bool");
Ed Tanous1ef4c342022-05-12 16:12:36 -07002036 messages::internalError(asyncResp->res);
2037 return;
2038 }
2039 asyncResp->res.jsonValue["Locked"] = *userLocked;
Ed Tanous20fa6a22024-05-20 18:02:58 -07002040 nlohmann::json::array_t allowed;
2041 // can only unlock accounts
2042 allowed.emplace_back("false");
Ed Tanous1ef4c342022-05-12 16:12:36 -07002043 asyncResp->res
Ed Tanous20fa6a22024-05-20 18:02:58 -07002044 .jsonValue["Locked@Redfish.AllowableValues"] =
2045 std::move(allowed);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002046 }
2047 else if (property.first == "UserPrivilege")
2048 {
2049 const std::string* userPrivPtr =
2050 std::get_if<std::string>(&property.second);
2051 if (userPrivPtr == nullptr)
2052 {
Ed Tanous62598e32023-07-17 17:06:25 -07002053 BMCWEB_LOG_ERROR("UserPrivilege wasn't a "
2054 "string");
Ed Tanous1ef4c342022-05-12 16:12:36 -07002055 messages::internalError(asyncResp->res);
2056 return;
2057 }
2058 std::string role = getRoleIdFromPrivilege(*userPrivPtr);
2059 if (role.empty())
2060 {
Ed Tanous62598e32023-07-17 17:06:25 -07002061 BMCWEB_LOG_ERROR("Invalid user role");
Ed Tanous1ef4c342022-05-12 16:12:36 -07002062 messages::internalError(asyncResp->res);
2063 return;
2064 }
2065 asyncResp->res.jsonValue["RoleId"] = role;
2066
2067 nlohmann::json& roleEntry =
2068 asyncResp->res.jsonValue["Links"]["Role"];
Ed Tanous3b327802023-08-14 09:23:43 -07002069 roleEntry["@odata.id"] = boost::urls::format(
2070 "/redfish/v1/AccountService/Roles/{}", role);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002071 }
2072 else if (property.first == "UserPasswordExpired")
2073 {
2074 const bool* userPasswordExpired =
2075 std::get_if<bool>(&property.second);
2076 if (userPasswordExpired == nullptr)
2077 {
Ed Tanous62598e32023-07-17 17:06:25 -07002078 BMCWEB_LOG_ERROR(
2079 "UserPasswordExpired wasn't a bool");
Ed Tanous1ef4c342022-05-12 16:12:36 -07002080 messages::internalError(asyncResp->res);
2081 return;
2082 }
2083 asyncResp->res.jsonValue["PasswordChangeRequired"] =
2084 *userPasswordExpired;
2085 }
Abhishek Patelc7229812022-02-01 10:07:15 -06002086 else if (property.first == "UserGroups")
2087 {
2088 const std::vector<std::string>* userGroups =
2089 std::get_if<std::vector<std::string>>(
2090 &property.second);
2091 if (userGroups == nullptr)
2092 {
Ed Tanous62598e32023-07-17 17:06:25 -07002093 BMCWEB_LOG_ERROR(
2094 "userGroups wasn't a string vector");
Abhishek Patelc7229812022-02-01 10:07:15 -06002095 messages::internalError(asyncResp->res);
2096 return;
2097 }
2098 if (!translateUserGroup(*userGroups, asyncResp->res))
2099 {
Ed Tanous62598e32023-07-17 17:06:25 -07002100 BMCWEB_LOG_ERROR("userGroups mapping failed");
Abhishek Patelc7229812022-02-01 10:07:15 -06002101 messages::internalError(asyncResp->res);
2102 return;
2103 }
2104 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002105 }
2106 }
2107 }
2108
Ed Tanous3b327802023-08-14 09:23:43 -07002109 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
2110 "/redfish/v1/AccountService/Accounts/{}", accountName);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002111 asyncResp->res.jsonValue["Id"] = accountName;
2112 asyncResp->res.jsonValue["UserName"] = accountName;
Patrick Williams5a39f772023-10-20 11:20:21 -05002113 });
Ed Tanous1ef4c342022-05-12 16:12:36 -07002114}
2115
2116inline void
Gunnar Mills20fc3072023-01-27 15:13:36 -06002117 handleAccountDelete(App& app, const crow::Request& req,
2118 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2119 const std::string& username)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002120{
2121 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2122 {
2123 return;
2124 }
2125
Ed Tanous25b54db2024-04-17 15:40:31 -07002126 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2127 {
2128 // If authentication is disabled, there are no user accounts
2129 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2130 return;
2131 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002132 sdbusplus::message::object_path tempObjPath(rootUserDbusPath);
2133 tempObjPath /= username;
2134 const std::string userPath(tempObjPath);
2135
2136 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002137 [asyncResp, username](const boost::system::error_code& ec) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07002138 if (ec)
2139 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08002140 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
Ed Tanous1ef4c342022-05-12 16:12:36 -07002141 username);
2142 return;
2143 }
2144
2145 messages::accountRemoved(asyncResp->res);
Patrick Williams5a39f772023-10-20 11:20:21 -05002146 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002147 "xyz.openbmc_project.User.Manager", userPath,
2148 "xyz.openbmc_project.Object.Delete", "Delete");
2149}
2150
2151inline void
2152 handleAccountPatch(App& app, const crow::Request& req,
2153 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2154 const std::string& username)
2155{
2156 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2157 {
2158 return;
2159 }
Ed Tanous25b54db2024-04-17 15:40:31 -07002160 if constexpr (BMCWEB_INSECURE_DISABLE_AUTH)
2161 {
2162 // If authentication is disabled, there are no user accounts
2163 messages::resourceNotFound(asyncResp->res, "ManagerAccount", username);
2164 return;
2165 }
Ed Tanous1ef4c342022-05-12 16:12:36 -07002166 std::optional<std::string> newUserName;
2167 std::optional<std::string> password;
2168 std::optional<bool> enabled;
2169 std::optional<std::string> roleId;
2170 std::optional<bool> locked;
Abhishek Patel58345852022-02-02 08:54:25 -06002171 std::optional<std::vector<std::string>> accountTypes;
2172
Ed Tanous1ef4c342022-05-12 16:12:36 -07002173 if (req.session == nullptr)
2174 {
2175 messages::internalError(asyncResp->res);
2176 return;
2177 }
2178
Ed Tanous2b9c1df2024-04-06 13:52:01 -07002179 bool userSelf = (username == req.session->username);
2180
Ed Tanous1ef4c342022-05-12 16:12:36 -07002181 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -05002182 redfish::getUserPrivileges(*req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002183 Privileges configureUsers = {"ConfigureUsers"};
2184 bool userHasConfigureUsers =
2185 effectiveUserPrivileges.isSupersetOf(configureUsers);
2186 if (userHasConfigureUsers)
2187 {
2188 // Users with ConfigureUsers can modify for all users
Abhishek Patel58345852022-02-02 08:54:25 -06002189 if (!json_util::readJsonPatch(
2190 req, asyncResp->res, "UserName", newUserName, "Password",
2191 password, "RoleId", roleId, "Enabled", enabled, "Locked",
2192 locked, "AccountTypes", accountTypes))
Ed Tanous1ef4c342022-05-12 16:12:36 -07002193 {
2194 return;
2195 }
2196 }
2197 else
2198 {
2199 // ConfigureSelf accounts can only modify their own account
Abhishek Patel58345852022-02-02 08:54:25 -06002200 if (!userSelf)
Ed Tanous1ef4c342022-05-12 16:12:36 -07002201 {
2202 messages::insufficientPrivilege(asyncResp->res);
2203 return;
2204 }
2205
2206 // ConfigureSelf accounts can only modify their password
2207 if (!json_util::readJsonPatch(req, asyncResp->res, "Password",
2208 password))
2209 {
2210 return;
2211 }
2212 }
2213
2214 // if user name is not provided in the patch method or if it
2215 // matches the user name in the URI, then we are treating it as
2216 // updating user properties other then username. If username
2217 // provided doesn't match the URI, then we are treating this as
2218 // user rename request.
2219 if (!newUserName || (newUserName.value() == username))
2220 {
2221 updateUserProperties(asyncResp, username, password, enabled, roleId,
Ravi Tejae518ef32024-05-16 10:33:08 -05002222 locked, accountTypes, userSelf, req.session);
Ed Tanous1ef4c342022-05-12 16:12:36 -07002223 return;
2224 }
2225 crow::connections::systemBus->async_method_call(
2226 [asyncResp, username, password(std::move(password)),
2227 roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)},
Ravi Tejae518ef32024-05-16 10:33:08 -05002228 locked, userSelf, req, accountTypes(std::move(accountTypes))](
Ed Tanouse81de512023-06-27 17:07:00 -07002229 const boost::system::error_code& ec, sdbusplus::message_t& m) {
Ed Tanous1ef4c342022-05-12 16:12:36 -07002230 if (ec)
2231 {
2232 userErrorMessageHandler(m.get_error(), asyncResp, newUser,
2233 username);
2234 return;
2235 }
2236
2237 updateUserProperties(asyncResp, newUser, password, enabled, roleId,
Ravi Tejae518ef32024-05-16 10:33:08 -05002238 locked, accountTypes, userSelf, req.session);
Patrick Williams5a39f772023-10-20 11:20:21 -05002239 },
Ed Tanous1ef4c342022-05-12 16:12:36 -07002240 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
2241 "xyz.openbmc_project.User.Manager", "RenameUser", username,
2242 *newUserName);
2243}
2244
Ed Tanous6c51eab2021-06-03 12:30:29 -07002245inline void requestAccountServiceRoutes(App& app)
Ed Tanousb9b2e0b2018-09-13 13:47:50 -07002246{
Ed Tanous6c51eab2021-06-03 12:30:29 -07002247 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002248 .privileges(redfish::privileges::headAccountService)
2249 .methods(boost::beast::http::verb::head)(
2250 std::bind_front(handleAccountServiceHead, std::ref(app)));
2251
2252 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Ed Tanoused398212021-06-09 17:05:54 -07002253 .privileges(redfish::privileges::getAccountService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002254 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002255 std::bind_front(handleAccountServiceGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002256
Ed Tanousf5ffd802021-07-19 10:55:33 -07002257 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/")
Gunnar Mills1ec43ee2022-01-04 15:39:52 -06002258 .privileges(redfish::privileges::patchAccountService)
Ed Tanousf5ffd802021-07-19 10:55:33 -07002259 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002260 std::bind_front(handleAccountServicePatch, std::ref(app)));
Ed Tanousf5ffd802021-07-19 10:55:33 -07002261
Ed Tanous1aa375b2024-04-13 11:51:10 -07002262 BMCWEB_ROUTE(
2263 app,
2264 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates")
2265 .privileges(redfish::privileges::headCertificateCollection)
2266 .methods(boost::beast::http::verb::head)(std::bind_front(
2267 handleAccountServiceClientCertificatesHead, std::ref(app)));
2268
2269 BMCWEB_ROUTE(
2270 app,
2271 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates")
2272 .privileges(redfish::privileges::getCertificateCollection)
2273 .methods(boost::beast::http::verb::get)(std::bind_front(
2274 handleAccountServiceClientCertificatesGet, std::ref(app)));
2275
2276 BMCWEB_ROUTE(
2277 app,
2278 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>")
2279 .privileges(redfish::privileges::headCertificate)
2280 .methods(boost::beast::http::verb::head)(std::bind_front(
2281 handleAccountServiceClientCertificatesInstanceHead, std::ref(app)));
2282
2283 BMCWEB_ROUTE(
2284 app,
2285 "/redfish/v1/AccountService/MultiFactorAuth/ClientCertificate/Certificates/<str>/")
2286 .privileges(redfish::privileges::getCertificate)
2287 .methods(boost::beast::http::verb::get)(std::bind_front(
2288 handleAccountServiceClientCertificatesInstanceGet, std::ref(app)));
2289
Ed Tanous6c51eab2021-06-03 12:30:29 -07002290 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002291 .privileges(redfish::privileges::headManagerAccountCollection)
2292 .methods(boost::beast::http::verb::head)(
2293 std::bind_front(handleAccountCollectionHead, std::ref(app)));
2294
2295 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002296 .privileges(redfish::privileges::getManagerAccountCollection)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002297 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002298 std::bind_front(handleAccountCollectionGet, std::ref(app)));
Ed Tanous06e086d2018-09-19 17:19:52 -07002299
Ed Tanous6c51eab2021-06-03 12:30:29 -07002300 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
Ed Tanoused398212021-06-09 17:05:54 -07002301 .privileges(redfish::privileges::postManagerAccountCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002302 .methods(boost::beast::http::verb::post)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002303 std::bind_front(handleAccountCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07002304
2305 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous4c7d4d32022-07-07 15:29:35 -07002306 .privileges(redfish::privileges::headManagerAccount)
2307 .methods(boost::beast::http::verb::head)(
2308 std::bind_front(handleAccountHead, std::ref(app)));
2309
2310 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanous002d39b2022-05-31 08:59:27 -07002311 .privileges(redfish::privileges::getManagerAccount)
2312 .methods(boost::beast::http::verb::get)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002313 std::bind_front(handleAccountGet, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002314
2315 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002316 // TODO this privilege should be using the generated endpoints, but
2317 // because of the special handling of ConfigureSelf, it's not able to
2318 // yet
Ed Tanous6c51eab2021-06-03 12:30:29 -07002319 .privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
2320 .methods(boost::beast::http::verb::patch)(
Ed Tanous1ef4c342022-05-12 16:12:36 -07002321 std::bind_front(handleAccountPatch, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002322
2323 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002324 .privileges(redfish::privileges::deleteManagerAccount)
Ed Tanous6c51eab2021-06-03 12:30:29 -07002325 .methods(boost::beast::http::verb::delete_)(
Gunnar Mills20fc3072023-01-27 15:13:36 -06002326 std::bind_front(handleAccountDelete, std::ref(app)));
Ed Tanous6c51eab2021-06-03 12:30:29 -07002327}
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +01002328
Ed Tanous1abe55e2018-09-05 08:30:59 -07002329} // namespace redfish