blob: 1f88bb3b7ad9c667642db8c60a9ee67919f2a10a [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 Tanousb9b2e0b2018-09-13 13:47:50 -070019#include <openbmc_dbus_rest.hpp>
Ed Tanousa8408792018-09-05 16:08:38 -070020#include <utils/json_utils.hpp>
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070021
Ed Tanous1abe55e2018-09-05 08:30:59 -070022namespace redfish
23{
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010024
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070025using ManagedObjectType = std::vector<std::pair<
26 sdbusplus::message::object_path,
27 boost::container::flat_map<
28 std::string, boost::container::flat_map<
29 std::string, sdbusplus::message::variant<bool>>>>>;
30
Ed Tanous1abe55e2018-09-05 08:30:59 -070031class AccountService : public Node
32{
33 public:
34 AccountService(CrowApp& app) : Node(app, "/redfish/v1/AccountService/")
35 {
36 Node::json["@odata.id"] = "/redfish/v1/AccountService";
37 Node::json["@odata.type"] = "#AccountService.v1_1_0.AccountService";
38 Node::json["@odata.context"] =
39 "/redfish/v1/$metadata#AccountService.AccountService";
40 Node::json["Id"] = "AccountService";
41 Node::json["Description"] = "BMC User Accounts";
42 Node::json["Name"] = "Account Service";
43 Node::json["ServiceEnabled"] = true;
44 Node::json["MinPasswordLength"] = 1;
45 Node::json["MaxPasswordLength"] = 20;
46 Node::json["Accounts"]["@odata.id"] =
47 "/redfish/v1/AccountService/Accounts";
48 Node::json["Roles"]["@odata.id"] = "/redfish/v1/AccountService/Roles";
Ed Tanous3ebd75f2018-03-05 18:20:01 -080049
Ed Tanous1abe55e2018-09-05 08:30:59 -070050 entityPrivileges = {
51 {boost::beast::http::verb::get,
52 {{"ConfigureUsers"}, {"ConfigureManager"}}},
53 {boost::beast::http::verb::head, {{"Login"}}},
54 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
55 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
56 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
57 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
58 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010059
Ed Tanous1abe55e2018-09-05 08:30:59 -070060 private:
61 void doGet(crow::Response& res, const crow::Request& req,
62 const std::vector<std::string>& params) override
63 {
64 res.jsonValue = Node::json;
65 res.end();
66 }
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +010067};
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070068class AccountsCollection : public Node
69{
70 public:
71 AccountsCollection(CrowApp& app) :
72 Node(app, "/redfish/v1/AccountService/Accounts/")
73 {
74
75 Node::json = {{"@odata.context", "/redfish/v1/"
76 "$metadata#ManagerAccountCollection."
77 "ManagerAccountCollection"},
78 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
79 {"@odata.type", "#ManagerAccountCollection."
80 "ManagerAccountCollection"},
81 {"Name", "Accounts Collection"},
82 {"Description", "BMC User Accounts"}};
83
84 entityPrivileges = {
85 {boost::beast::http::verb::get,
86 {{"ConfigureUsers"}, {"ConfigureManager"}}},
87 {boost::beast::http::verb::head, {{"Login"}}},
88 {boost::beast::http::verb::patch, {{"ConfigureUsers"}}},
89 {boost::beast::http::verb::put, {{"ConfigureUsers"}}},
90 {boost::beast::http::verb::delete_, {{"ConfigureUsers"}}},
91 {boost::beast::http::verb::post, {{"ConfigureUsers"}}}};
92 }
93
94 private:
95 void doGet(crow::Response& res, const crow::Request& req,
96 const std::vector<std::string>& params) override
97 {
98 res.jsonValue = Node::json;
99 auto asyncResp = std::make_shared<AsyncResp>(res);
100 crow::connections::systemBus->async_method_call(
101 [asyncResp](const boost::system::error_code ec,
102 const ManagedObjectType& users) {
103 if (ec)
104 {
105 asyncResp->res.result(
106 boost::beast::http::status::internal_server_error);
107 return;
108 }
109
110 nlohmann::json& memberArray =
111 asyncResp->res.jsonValue["Members"];
112 memberArray = nlohmann::json::array();
113
114 asyncResp->res.jsonValue["Members@odata.count"] = users.size();
115 for (auto& user : users)
116 {
117 const std::string& path =
118 static_cast<const std::string&>(user.first);
119 std::size_t lastIndex = path.rfind("/");
120 if (lastIndex == std::string::npos)
121 {
122 lastIndex = 0;
123 }
124 else
125 {
126 lastIndex += 1;
127 }
128 memberArray.push_back(
129 {{"@odata.id", "/redfish/v1/AccountService/Accounts/" +
130 path.substr(lastIndex)}});
131 }
132 },
133 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
134 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
135 }
136};
137
Ed Tanousa8408792018-09-05 16:08:38 -0700138template <typename Callback>
139inline void checkDbusPathExists(const std::string& path, Callback&& callback)
140{
141 using GetObjectType =
142 std::vector<std::pair<std::string, std::vector<std::string>>>;
143
144 crow::connections::systemBus->async_method_call(
145 [callback{std::move(callback)}](const boost::system::error_code ec,
146 const GetObjectType& object_names) {
147 callback(ec || object_names.size() == 0);
148 },
149 "xyz.openbmc_project.ObjectMapper",
150 "/xyz/openbmc_project/object_mapper",
151 "xyz.openbmc_project.ObjectMapper", "GetObject", path,
152 std::array<std::string, 0>());
153}
154
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700155class ManagerAccount : public Node
156{
157 public:
158 ManagerAccount(CrowApp& app) :
159 Node(app, "/redfish/v1/AccountService/Accounts/<str>/", std::string())
160 {
161 Node::json = {{"@odata.context",
162 "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
163 {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
164
165 {"Name", "User Account"},
166 {"Description", "User Account"},
167 {"Enabled", false},
168 {"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 {
224 asyncResp->res.jsonValue["@odata.id"] =
225 "/redfish/v1/AccountService/Accounts/" +
226 accountName;
227 asyncResp->res.jsonValue["Id"] = accountName;
228 asyncResp->res.jsonValue["UserName"] = accountName;
229
230 return;
231 }
232 }
233
234 asyncResp->res.result(boost::beast::http::status::not_found);
235 },
236 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
237 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
238 }
Ed Tanousa8408792018-09-05 16:08:38 -0700239
240 void doPatch(crow::Response& res, const crow::Request& req,
241 const std::vector<std::string>& params) override
242 {
243 auto asyncResp = std::make_shared<AsyncResp>(res);
244
245 if (params.size() != 1)
246 {
247 res.result(boost::beast::http::status::internal_server_error);
248 return;
249 }
250
251 nlohmann::json patchRequest;
252 if (!json_util::processJsonFromRequest(res, req, patchRequest))
253 {
254 return;
255 }
256
257 // Check the user exists before updating the fields
258 checkDbusPathExists(
259 "/xyz/openbmc_project/users/" + params[0],
260 [username{std::string(params[0])},
261 patchRequest(std::move(patchRequest)),
262 asyncResp](bool userExists) {
263 if (!userExists)
264 {
265 messages::addMessageToErrorJson(
266 asyncResp->res.jsonValue,
267 messages::resourceNotFound(
268 "#ManagerAccount.v1_0_3.ManagerAccount", username));
269
270 asyncResp->res.result(
271 boost::beast::http::status::not_found);
272 return;
273 }
274
275 for (const auto& item : patchRequest.items())
276 {
277 if (item.key() == "Password")
278 {
279 const std::string* passStr =
280 item.value().get_ptr<const std::string*>();
281 if (passStr == nullptr)
282 {
283 messages::addMessageToErrorJson(
284 asyncResp->res.jsonValue,
285 messages::propertyValueFormatError(
286 item.value().dump(), "Password"));
287 return;
288 }
289 BMCWEB_LOG_DEBUG << "Updating user: " << username
290 << " to password " << *passStr;
291 if (!pamUpdatePassword(username, *passStr))
292 {
293 BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
294 asyncResp->res.result(boost::beast::http::status::
295 internal_server_error);
296 return;
297 }
298 }
299 else if (item.key() == "Enabled")
300 {
301 const bool* enabledBool =
302 item.value().get_ptr<const bool*>();
303
304 if (enabledBool == nullptr)
305 {
306 messages::addMessageToErrorJson(
307 asyncResp->res.jsonValue,
308 messages::propertyValueFormatError(
309 item.value().dump(), "Enabled"));
310 return;
311 }
312 crow::connections::systemBus->async_method_call(
313 [asyncResp](const boost::system::error_code ec) {
314 if (ec)
315 {
316 BMCWEB_LOG_ERROR
317 << "D-Bus responses error: " << ec;
318 asyncResp->res.result(
319 boost::beast::http::status::
320 internal_server_error);
321 return;
322 }
323 // TODO Consider support polling mechanism to
324 // verify status of host and chassis after
325 // execute the requested action.
326 BMCWEB_LOG_DEBUG << "Response with no content";
327 asyncResp->res.result(
328 boost::beast::http::status::no_content);
329 },
330 "xyz.openbmc_project.User.Manager",
331 "/xyz/openbmc_project/users/" + username,
332 "org.freedesktop.DBus.Properties", "Set",
333 "xyz.openbmc_project.User.Attributes"
334 "UserEnabled",
335 sdbusplus::message::variant<bool>{*enabledBool});
336 }
337 else
338 {
339 messages::addMessageToErrorJson(
340 asyncResp->res.jsonValue,
341 messages::propertyNotWritable(item.key()));
342 asyncResp->res.result(
343 boost::beast::http::status::bad_request);
344 return;
345 }
346 }
347 });
348 }
Ed Tanousb9b2e0b2018-09-13 13:47:50 -0700349};
Lewanczyk, Dawid88d16c92018-02-02 14:51:09 +0100350
Ed Tanous1abe55e2018-09-05 08:30:59 -0700351} // namespace redfish