blob: d44d4a3b8f6a97730671666c1f4bbc18090e9b3c [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 {
37 Node::json["@odata.id"] = "/redfish/v1/AccountService";
38 Node::json["@odata.type"] = "#AccountService.v1_1_0.AccountService";
39 Node::json["@odata.context"] =
40 "/redfish/v1/$metadata#AccountService.AccountService";
41 Node::json["Id"] = "AccountService";
42 Node::json["Description"] = "BMC User Accounts";
43 Node::json["Name"] = "Account Service";
44 Node::json["ServiceEnabled"] = true;
45 Node::json["MinPasswordLength"] = 1;
46 Node::json["MaxPasswordLength"] = 20;
47 Node::json["Accounts"]["@odata.id"] =
48 "/redfish/v1/AccountService/Accounts";
49 Node::json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
Ed Tanous3ebd75f2018-03-05 18:20:01 -080050
Ed Tanous1abe55e2018-09-05 08:30:59 -070051 entityPrivileges = {
52 {boost::beast::http::verb::get,
53 {{"ConfigureUsers"}, {"ConfigureManager"}}},
54 {boost::beast::http::verb::head, {{"Login"}}},
55 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
56 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
57 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
58 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
59 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010060
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 private:
62 void doGet(crow::Response& res, const crow::Request& req,
63 const std::vector<std::string>& params) override
64 {
65 res.jsonValue = Node::json;
66 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 {
75
76 Node::json = {{"@odata.context", "/redfish/v1/"
77 "$metadata#ManagerAccountCollection."
78 "ManagerAccountCollection"},
79 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
80 {"@odata.type", "#ManagerAccountCollection."
81 "ManagerAccountCollection"},
82 {"Name", "Accounts Collection"},
83 {"Description", "BMC User Accounts"}};
84
85 entityPrivileges = {
86 {boost::beast::http::verb::get,
87 {{"ConfigureUsers"}, {"ConfigureManager"}}},
88 {boost::beast::http::verb::head, {{"Login"}}},
89 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
90 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
91 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
92 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
93 }
94
95 private:
96 void doGet(crow::Response& res, const crow::Request& req,
97 const std::vector<std::string>& params) override
98 {
99 res.jsonValue = Node::json;
100 auto asyncResp = std::make_shared<AsyncResp>(res);
101 crow::connections::systemBus->async_method_call(
102 [asyncResp](const boost::system::error_code ec,
103 const ManagedObjectType& users) {
104 if (ec)
105 {
106 asyncResp->res.result(
107 boost::beast::http::status::internal_server_error);
108 return;
109 }
110
111 nlohmann::json& memberArray =
112 asyncResp->res.jsonValue["Members"];
113 memberArray = nlohmann::json::array();
114
115 asyncResp->res.jsonValue["Members@odata.count"] = users.size();
116 for (auto& user : users)
117 {
118 const std::string& path =
119 static_cast<const std::string&>(user.first);
120 std::size_t lastIndex = path.rfind("/");
121 if (lastIndex == std::string::npos)
122 {
123 lastIndex = 0;
124 }
125 else
126 {
127 lastIndex += 1;
128 }
129 memberArray.push_back(
130 {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
131 path.substr(lastIndex)}});
132 }
133 },
134 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
135 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
136 }
137};
138
Ed Tanousa8408792018-09-05 16:08:38 -0700139template <typename Callback>
140inline void checkDbusPathExists(const std::string& path, Callback&& callback)
141{
142 using GetObjectType =
143 std::vector<std::pair<std::string, std::vector<std::string>>>;
144
145 crow::connections::systemBus->async_method_call(
146 [callback{std::move(callback)}](const boost::system::error_code ec,
147 const GetObjectType& object_names) {
148 callback(ec || object_names.size() == 0);
149 },
150 "xyz.openbmc_project.ObjectMapper",
151 "/xyz/openbmc_project/object_mapper",
152 "xyz.openbmc_project.ObjectMapper", "GetObject", path,
153 std::array<std::string, 0>());
154}
155
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700156class ManagerAccount : public Node
157{
158 public:
159 ManagerAccount(CrowApp& app) :
160 Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
161 {
162 Node::json = {{"@odata.context",
163 "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
164 {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
165
166 {"Name", "User Account"},
167 {"Description", "User Account"},
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700168 {"Password", nullptr},
169 {"RoleId", "Administrator"},
170 {"Links",
171 {{"Role",
172 {{"@odata.id", "/redfish/v1/AccountService/Roles/"
173 "Administrator"}}}}}};
174
175 entityPrivileges = {
176 {boost::beast::http::verb::get,
177 {{"ConfigureUsers"}, {"ConfigureManager"}, {"ConfigureSelf"}}},
178 {boost::beast::http::verb::head, {{"Login"}}},
179 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
180 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
181 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
182 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
183 }
184
185 private:
186 void doGet(crow::Response& res, const crow::Request& req,
187 const std::vector<std::string>& params) override
188 {
189 res.jsonValue = Node::json;
190 auto asyncResp = std::make_shared<AsyncResp>(res);
191
192 if (params.size() != 1)
193 {
194 res.result(boost::beast::http::status::internal_server_error);
195 return;
196 }
197
198 crow::connections::systemBus->async_method_call(
199 [asyncResp, accountName{std::string(params[0])}](
200 const boost::system::error_code ec,
201 const ManagedObjectType& users) {
202 if (ec)
203 {
204 asyncResp->res.result(
205 boost::beast::http::status::internal_server_error);
206 return;
207 }
208
209 for (auto& user : users)
210 {
211 const std::string& path =
212 static_cast<const std::string&>(user.first);
213 std::size_t lastIndex = path.rfind("/");
214 if (lastIndex == std::string::npos)
215 {
216 lastIndex = 0;
217 }
218 else
219 {
220 lastIndex += 1;
221 }
222 if (path.substr(lastIndex) == accountName)
223 {
Ed Tanous65b0dc32018-09-19 16:04:03 -0700224 for (const auto& interface : user.second)
225 {
226 if (interface.first ==
227 "xyz.openbmc_project.User.Attributes")
228 {
229 for (const auto& property : interface.second)
230 {
231 if (property.first == "UserEnabled")
232 {
233 const bool* userEnabled =
234 mapbox::getPtr<const bool>(
235 property.second);
236 if (userEnabled == nullptr)
237 {
238 BMCWEB_LOG_ERROR
239 << "UserEnabled wasn't a bool";
240 continue;
241 }
242 asyncResp->res.jsonValue["Enabled"] =
243 *userEnabled;
244 }
245 else if (property.first ==
246 "UserLockedForFailedAttempt")
247 {
248 const bool* userLocked =
249 mapbox::getPtr<const bool>(
250 property.second);
251 if (userLocked == nullptr)
252 {
253 BMCWEB_LOG_ERROR
254 << "UserEnabled wasn't a bool";
255 continue;
256 }
257 asyncResp->res.jsonValue["Locked"] =
258 *userLocked;
259 }
260 }
261 }
262 }
263
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700264 asyncResp->res.jsonValue["@odata.id"] =
265 "/redfish/v1/AccountService/Accounts/" +
266 accountName;
267 asyncResp->res.jsonValue["Id"] = accountName;
268 asyncResp->res.jsonValue["UserName"] = accountName;
269
270 return;
271 }
272 }
273
274 asyncResp->res.result(boost::beast::http::status::not_found);
275 },
276 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
277 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
278 }
Ed Tanousa8408792018-09-05 16:08:38 -0700279
280 void doPatch(crow::Response& res, const crow::Request& req,
281 const std::vector<std::string>& params) override
282 {
283 auto asyncResp = std::make_shared<AsyncResp>(res);
284
285 if (params.size() != 1)
286 {
287 res.result(boost::beast::http::status::internal_server_error);
288 return;
289 }
290
291 nlohmann::json patchRequest;
292 if (!json_util::processJsonFromRequest(res, req, patchRequest))
293 {
294 return;
295 }
296
297 // Check the user exists before updating the fields
298 checkDbusPathExists(
299 "/xyz/openbmc_project/users/" + params[0],
300 [username{std::string(params[0])},
301 patchRequest(std::move(patchRequest)),
302 asyncResp](bool userExists) {
303 if (!userExists)
304 {
305 messages::addMessageToErrorJson(
306 asyncResp->res.jsonValue,
307 messages::resourceNotFound(
308 "#ManagerAccount.v1_0_3.ManagerAccount", username));
309
310 asyncResp->res.result(
311 boost::beast::http::status::not_found);
312 return;
313 }
314
315 for (const auto& item : patchRequest.items())
316 {
317 if (item.key() == "Password")
318 {
319 const std::string* passStr =
320 item.value().get_ptr<const std::string*>();
321 if (passStr == nullptr)
322 {
323 messages::addMessageToErrorJson(
324 asyncResp->res.jsonValue,
325 messages::propertyValueFormatError(
326 item.value().dump(), "Password"));
327 return;
328 }
329 BMCWEB_LOG_DEBUG << "Updating user: " << username
330 << " to password " << *passStr;
331 if (!pamUpdatePassword(username, *passStr))
332 {
333 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
334 asyncResp->res.result(boost::beast::http::status::
335 internal_server_error);
336 return;
337 }
338 }
339 else if (item.key() == "Enabled")
340 {
341 const bool* enabledBool =
342 item.value().get_ptr<const bool*>();
343
344 if (enabledBool == nullptr)
345 {
346 messages::addMessageToErrorJson(
347 asyncResp->res.jsonValue,
348 messages::propertyValueFormatError(
349 item.value().dump(), "Enabled"));
350 return;
351 }
352 crow::connections::systemBus->async_method_call(
353 [asyncResp](const boost::system::error_code ec) {
354 if (ec)
355 {
356 BMCWEB_LOG_ERROR
357 << "D-Bus responses error: " << ec;
358 asyncResp->res.result(
359 boost::beast::http::status::
360 internal_server_error);
361 return;
362 }
363 // TODO Consider support polling mechanism to
364 // verify status of host and chassis after
365 // execute the requested action.
366 BMCWEB_LOG_DEBUG << "Response with no content";
367 asyncResp->res.result(
368 boost::beast::http::status::no_content);
369 },
370 "xyz.openbmc_project.User.Manager",
371 "/xyz/openbmc_project/users/" + username,
372 "org.freedesktop.DBus.Properties", "Set",
373 "xyz.openbmc_project.User.Attributes"
374 "UserEnabled",
375 sdbusplus::message::variant<bool>{*enabledBool});
376 }
377 else
378 {
379 messages::addMessageToErrorJson(
380 asyncResp->res.jsonValue,
381 messages::propertyNotWritable(item.key()));
382 asyncResp->res.result(
383 boost::beast::http::status::bad_request);
384 return;
385 }
386 }
387 });
388 }
Ed Tanous06e086d2018-09-19 17:19:52 -0700389
390 void doDelete(crow::Response& res, const crow::Request& req,
391 const std::vector<std::string>& params) override
392 {
393 auto asyncResp = std::make_shared<AsyncResp>(res);
394
395 if (params.size() != 1)
396 {
397 res.result(boost::beast::http::status::internal_server_error);
398 return;
399 }
400
401 const std::string userPath = "/xyz/openbmc_project/user/" + params[0];
402
403 crow::connections::systemBus->async_method_call(
404 [asyncResp, username{std::move(params[0])}](
405 const boost::system::error_code ec) {
406 if (ec)
407 {
408 messages::addMessageToErrorJson(
409 asyncResp->res.jsonValue,
410 messages::resourceNotFound(
411 "#ManagerAccount.v1_0_3.ManagerAccount", username));
412 asyncResp->res.result(
413 boost::beast::http::status::not_found);
414 return;
415 }
416
417 messages::addMessageToJsonRoot(asyncResp->res.jsonValue,
418 messages::accountRemoved());
419 },
420 "xyz.openbmc_project.User.Manager", userPath,
421 "xyz.openbmc_project.Object.Delete", "Delete");
422 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700423};
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +0100424
Ed Tanous1abe55e2018-09-05 08:30:59 -0700425} // namespace redfish