blob: a93407aa74adb532dab9ac26beff66cfe98f8810 [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#include "node.hpp"
18
Ed Tanous65b0dc32018-09-19 16:04:03 -070019#include <error_messages.hpp>
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070020#include <openbmc_dbus_rest.hpp>
Ed Tanousa8408792018-09-05 16:08:38 -070021#include <utils/json_utils.hpp>
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070022
Ed Tanous1abe55e2018-09-05 08:30:59 -070023namespace redfish
24{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010025
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070026using ManagedObjectType = std::vector<std::pair<
27 sdbusplus::message::object_path,
28 boost::container::flat_map<
29 std::string, boost::container::flat_map<
30 std::string, sdbusplus::message::variant<bool>>>>>;
31
Ed Tanous1abe55e2018-09-05 08:30:59 -070032class AccountService : public Node
33{
34 public:
35 AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/")
36 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070037 entityPrivileges = {
38 {boost::beast::http::verb::get,
39 {{"ConfigureUsers"}, {"ConfigureManager"}}},
40 {boost::beast::http::verb::head, {{"Login"}}},
41 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
42 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
43 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
44 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
45 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010046
Ed Tanous1abe55e2018-09-05 08:30:59 -070047 private:
48 void doGet(crow::Response& res, const crow::Request& req,
49 const std::vector<std::string>& params) override
50 {
Ed Tanous0f74e642018-11-12 15:17:05 -080051 res.jsonValue["@odata.id"] = "/redfish/v1/AccountService";
52 res.jsonValue["@odata.type"] = "#AccountService.v1_1_0.AccountService";
53 res.jsonValue["@odata.context"] =
54 "/redfish/v1/$metadata#AccountService.AccountService";
55 res.jsonValue["Id"] = "AccountService";
56 res.jsonValue["Description"] = "BMC User Accounts";
57 res.jsonValue["Name"] = "Account Service";
58 res.jsonValue["ServiceEnabled"] = true;
59 res.jsonValue["MinPasswordLength"] = 1;
60 res.jsonValue["MaxPasswordLength"] = 20;
61 res.jsonValue["Accounts"]["@odata.id"] =
62 "/redfish/v1/AccountService/Accounts";
63 res.jsonValue["Roles"]["@odata.id"] =
64 "/redfish/v1/AccountService/Roles";
65
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 res.end();
67 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010068};
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070069class AccountsCollection : public Node
70{
71 public:
72 AccountsCollection(CrowApp& app) :
73 Node(app, "/redfish/v1/AccountService/Accounts/")
74 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070075 entityPrivileges = {
76 {boost::beast::http::verb::get,
77 {{"ConfigureUsers"}, {"ConfigureManager"}}},
78 {boost::beast::http::verb::head, {{"Login"}}},
79 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
80 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
81 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
82 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
83 }
84
85 private:
86 void doGet(crow::Response& res, const crow::Request& req,
87 const std::vector<std::string>& params) override
88 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070089 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous0f74e642018-11-12 15:17:05 -080090 res.jsonValue = {{"@odata.context",
91 "/redfish/v1/"
92 "$metadata#ManagerAccountCollection."
93 "ManagerAccountCollection"},
94 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
95 {"@odata.type", "#ManagerAccountCollection."
96 "ManagerAccountCollection"},
97 {"Name", "Accounts Collection"},
98 {"Description", "BMC User Accounts"}};
99
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700100 crow::connections::systemBus->async_method_call(
101 [asyncResp](const boost::system::error_code ec,
102 const ManagedObjectType& users) {
103 if (ec)
104 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700105 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700106 return;
107 }
108
109 nlohmann::json& memberArray =
110 asyncResp->res.jsonValue["Members"];
111 memberArray = nlohmann::json::array();
112
113 asyncResp->res.jsonValue["Members@odata.count"] = users.size();
114 for (auto& user : users)
115 {
116 const std::string& path =
117 static_cast<const std::string&>(user.first);
118 std::size_t lastIndex = path.rfind("/");
119 if (lastIndex == std::string::npos)
120 {
121 lastIndex = 0;
122 }
123 else
124 {
125 lastIndex += 1;
126 }
127 memberArray.push_back(
128 {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
129 path.substr(lastIndex)}});
130 }
131 },
132 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
133 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
134 }
Ed Tanous04ae99e2018-09-20 15:54:36 -0700135 void doPost(crow::Response& res, const crow::Request& req,
136 const std::vector<std::string>& params) override
137 {
138 auto asyncResp = std::make_shared<AsyncResp>(res);
139
Ed Tanous9712f8a2018-09-21 13:38:49 -0700140 std::string username;
141 std::string password;
142 boost::optional<std::string> roleId("User");
143 boost::optional<bool> enabled = true;
144 if (!json_util::readJson(req, res, "UserName", username, "Password",
145 password, "RoleId", roleId, "Enabled",
146 enabled))
Ed Tanous04ae99e2018-09-20 15:54:36 -0700147 {
148 return;
149 }
150
Ed Tanous9712f8a2018-09-21 13:38:49 -0700151 const char* priv = getRoleIdFromPrivilege(*roleId);
152 if (priv == nullptr)
Ed Tanous04ae99e2018-09-20 15:54:36 -0700153 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700154 messages::propertyValueNotInList(asyncResp->res, *roleId, "RoleId");
Ed Tanous04ae99e2018-09-20 15:54:36 -0700155 return;
156 }
Ed Tanous9712f8a2018-09-21 13:38:49 -0700157 roleId = priv;
Ed Tanous04ae99e2018-09-20 15:54:36 -0700158
159 crow::connections::systemBus->async_method_call(
Ed Tanous9712f8a2018-09-21 13:38:49 -0700160 [asyncResp, username, password{std::move(password)}](
Ed Tanous04ae99e2018-09-20 15:54:36 -0700161 const boost::system::error_code ec) {
162 if (ec)
163 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700164 messages::resourceAlreadyExists(
165 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
166 "UserName", username);
Ed Tanous04ae99e2018-09-20 15:54:36 -0700167 return;
168 }
169
170 if (!pamUpdatePassword(username, password))
171 {
172 // At this point we have a user that's been created, but the
173 // password set failed. Something is wrong, so delete the
174 // user that we've already created
175 crow::connections::systemBus->async_method_call(
176 [asyncResp](const boost::system::error_code ec) {
177 if (ec)
178 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700179 messages::internalError(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -0700180 return;
181 }
182
Jason M. Billsf12894f2018-10-09 12:45:45 -0700183 messages::invalidObject(asyncResp->res, "Password");
Ed Tanous04ae99e2018-09-20 15:54:36 -0700184 },
185 "xyz.openbmc_project.User.Manager",
186 "/xyz/openbmc_project/user/" + username,
187 "xyz.openbmc_project.Object.Delete", "Delete");
188
189 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
190 return;
191 }
192
Jason M. Billsf12894f2018-10-09 12:45:45 -0700193 messages::created(asyncResp->res);
Ed Tanous04ae99e2018-09-20 15:54:36 -0700194 asyncResp->res.addHeader(
195 "Location",
196 "/redfish/v1/AccountService/Accounts/" + username);
197 },
198 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
Ed Tanous9712f8a2018-09-21 13:38:49 -0700199 "xyz.openbmc_project.User.Manager", "CreateUser", username,
Ed Tanous04ae99e2018-09-20 15:54:36 -0700200 std::array<const char*, 4>{"ipmi", "redfish", "ssh", "web"},
Ed Tanous9712f8a2018-09-21 13:38:49 -0700201 *roleId, *enabled);
Ed Tanous04ae99e2018-09-20 15:54:36 -0700202 }
203
204 static const char* getRoleIdFromPrivilege(boost::beast::string_view role)
205 {
206 if (role == "Administrator")
207 {
208 return "priv-admin";
209 }
210 else if (role == "Callback")
211 {
212 return "priv-callback";
213 }
214 else if (role == "User")
215 {
216 return "priv-user";
217 }
218 else if (role == "Operator")
219 {
220 return "priv-operator";
221 }
222 return nullptr;
223 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700224};
225
Ed Tanousa8408792018-09-05 16:08:38 -0700226template <typename Callback>
227inline void checkDbusPathExists(const std::string& path, Callback&& callback)
228{
229 using GetObjectType =
230 std::vector<std::pair<std::string, std::vector<std::string>>>;
231
232 crow::connections::systemBus->async_method_call(
233 [callback{std::move(callback)}](const boost::system::error_code ec,
234 const GetObjectType& object_names) {
235 callback(ec || object_names.size() == 0);
236 },
237 "xyz.openbmc_project.ObjectMapper",
238 "/xyz/openbmc_project/object_mapper",
239 "xyz.openbmc_project.ObjectMapper", "GetObject", path,
240 std::array<std::string, 0>());
241}
242
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700243class ManagerAccount : public Node
244{
245 public:
246 ManagerAccount(CrowApp& app) :
247 Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
248 {
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700249 entityPrivileges = {
250 {boost::beast::http::verb::get,
251 {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
252 {boost::beast::http::verb::head, {{"Login"}}},
253 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
254 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
255 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
256 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
257 }
258
259 private:
260 void doGet(crow::Response& res, const crow::Request& req,
261 const std::vector<std::string>& params) override
262 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800263 res.jsonValue = {
264 {"@odata.context",
265 "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
266 {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
267
268 {"Name", "User Account"},
269 {"Description", "User Account"},
270 {"Password", nullptr},
271 {"RoleId", "Administrator"},
272 {"Links",
273 {{"Role",
274 {{"@odata.id",
275 "/redfish/v1/AccountService/Roles/Administrator"}}}}}};
276
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700277 auto asyncResp = std::make_shared<AsyncResp>(res);
278
279 if (params.size() != 1)
280 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700281 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700282 return;
283 }
284
285 crow::connections::systemBus->async_method_call(
286 [asyncResp, accountName{std::string(params[0])}](
287 const boost::system::error_code ec,
288 const ManagedObjectType& users) {
289 if (ec)
290 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700291 messages::internalError(asyncResp->res);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700292 return;
293 }
294
295 for (auto& user : users)
296 {
297 const std::string& path =
298 static_cast<const std::string&>(user.first);
299 std::size_t lastIndex = path.rfind("/");
300 if (lastIndex == std::string::npos)
301 {
302 lastIndex = 0;
303 }
304 else
305 {
306 lastIndex += 1;
307 }
308 if (path.substr(lastIndex) == accountName)
309 {
Ed Tanous65b0dc32018-09-19 16:04:03 -0700310 for (const auto& interface : user.second)
311 {
312 if (interface.first ==
313 "xyz.openbmc_project.User.Attributes")
314 {
315 for (const auto& property : interface.second)
316 {
317 if (property.first == "UserEnabled")
318 {
319 const bool* userEnabled =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800320 sdbusplus::message::variant_ns::
321 get_if<bool>(&property.second);
Ed Tanous65b0dc32018-09-19 16:04:03 -0700322 if (userEnabled == nullptr)
323 {
324 BMCWEB_LOG_ERROR
325 << "UserEnabled wasn't a bool";
326 continue;
327 }
328 asyncResp->res.jsonValue["Enabled"] =
329 *userEnabled;
330 }
331 else if (property.first ==
332 "UserLockedForFailedAttempt")
333 {
334 const bool* userLocked =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800335 sdbusplus::message::variant_ns::
336 get_if<bool>(&property.second);
Ed Tanous65b0dc32018-09-19 16:04:03 -0700337 if (userLocked == nullptr)
338 {
339 BMCWEB_LOG_ERROR
340 << "UserEnabled wasn't a bool";
341 continue;
342 }
343 asyncResp->res.jsonValue["Locked"] =
344 *userLocked;
345 }
346 }
347 }
348 }
349
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700350 asyncResp->res.jsonValue["@odata.id"] =
351 "/redfish/v1/AccountService/Accounts/" +
352 accountName;
353 asyncResp->res.jsonValue["Id"] = accountName;
354 asyncResp->res.jsonValue["UserName"] = accountName;
355
356 return;
357 }
358 }
359
Jason M. Billsf12894f2018-10-09 12:45:45 -0700360 messages::resourceNotFound(asyncResp->res, "ManagerAccount",
361 accountName);
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700362 },
363 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
364 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
365 }
Ed Tanousa8408792018-09-05 16:08:38 -0700366
367 void doPatch(crow::Response& res, const crow::Request& req,
368 const std::vector<std::string>& params) override
369 {
370 auto asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousa8408792018-09-05 16:08:38 -0700371 if (params.size() != 1)
372 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700373 messages::internalError(asyncResp->res);
Ed Tanousa8408792018-09-05 16:08:38 -0700374 return;
375 }
376
Ed Tanous9712f8a2018-09-21 13:38:49 -0700377 boost::optional<std::string> password;
378 boost::optional<bool> enabled;
379 if (!json_util::readJson(req, res, "Password", password, "Enabled",
380 enabled))
Ed Tanousa8408792018-09-05 16:08:38 -0700381 {
382 return;
383 }
384
385 // Check the user exists before updating the fields
386 checkDbusPathExists(
387 "/xyz/openbmc_project/users/" + params[0],
Ed Tanous9712f8a2018-09-21 13:38:49 -0700388 [username{std::string(params[0])}, password(std::move(password)),
389 enabled(std::move(enabled)), asyncResp](bool userExists) {
Ed Tanousa8408792018-09-05 16:08:38 -0700390 if (!userExists)
391 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700392 messages::resourceNotFound(
393 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
394 username);
Ed Tanousa8408792018-09-05 16:08:38 -0700395 return;
396 }
397
Ed Tanous9712f8a2018-09-21 13:38:49 -0700398 if (password)
Ed Tanousa8408792018-09-05 16:08:38 -0700399 {
Ed Tanous9712f8a2018-09-21 13:38:49 -0700400 if (!pamUpdatePassword(username, *password))
Ed Tanousa8408792018-09-05 16:08:38 -0700401 {
Ed Tanous9712f8a2018-09-21 13:38:49 -0700402 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700403 messages::internalError(asyncResp->res);
Ed Tanousa8408792018-09-05 16:08:38 -0700404 return;
405 }
406 }
Ed Tanous9712f8a2018-09-21 13:38:49 -0700407
408 if (enabled)
409 {
410 crow::connections::systemBus->async_method_call(
411 [asyncResp](const boost::system::error_code ec) {
412 if (ec)
413 {
414 BMCWEB_LOG_ERROR << "D-Bus responses error: "
415 << ec;
Jason M. Billsf12894f2018-10-09 12:45:45 -0700416 messages::internalError(asyncResp->res);
Ed Tanous9712f8a2018-09-21 13:38:49 -0700417 return;
418 }
419 // TODO Consider support polling mechanism to
420 // verify status of host and chassis after
421 // execute the requested action.
Jason M. Billsf12894f2018-10-09 12:45:45 -0700422 messages::success(asyncResp->res);
Ed Tanous9712f8a2018-09-21 13:38:49 -0700423 },
424 "xyz.openbmc_project.User.Manager",
425 "/xyz/openbmc_project/users/" + username,
426 "org.freedesktop.DBus.Properties", "Set",
427 "xyz.openbmc_project.User.Attributes"
428 "UserEnabled",
429 sdbusplus::message::variant<bool>{*enabled});
430 }
Ed Tanousa8408792018-09-05 16:08:38 -0700431 });
432 }
Ed Tanous06e086d2018-09-19 17:19:52 -0700433
434 void doDelete(crow::Response& res, const crow::Request& req,
435 const std::vector<std::string>& params) override
436 {
437 auto asyncResp = std::make_shared<AsyncResp>(res);
438
439 if (params.size() != 1)
440 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700441 messages::internalError(asyncResp->res);
Ed Tanous06e086d2018-09-19 17:19:52 -0700442 return;
443 }
444
445 const std::string userPath = "/xyz/openbmc_project/user/" + params[0];
446
447 crow::connections::systemBus->async_method_call(
448 [asyncResp, username{std::move(params[0])}](
449 const boost::system::error_code ec) {
450 if (ec)
451 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700452 messages::resourceNotFound(
453 asyncResp->res, "#ManagerAccount.v1_0_3.ManagerAccount",
454 username);
Ed Tanous06e086d2018-09-19 17:19:52 -0700455 return;
456 }
457
Jason M. Billsf12894f2018-10-09 12:45:45 -0700458 messages::accountRemoved(asyncResp->res);
Ed Tanous06e086d2018-09-19 17:19:52 -0700459 },
460 "xyz.openbmc_project.User.Manager", userPath,
461 "xyz.openbmc_project.Object.Delete", "Delete");
462 }
Ed Tanous9712f8a2018-09-21 13:38:49 -0700463}; // namespace redfish
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +0100464
Ed Tanous1abe55e2018-09-05 08:30:59 -0700465} // namespace redfish