blob: e106079a8cd5dd343ca24388c085d4ee02001966 [file] [log] [blame]
Ed Tanous911ac312017-08-15 09:37:42 -07001#pragma once
2
Ed Tanous3dac7492017-08-02 13:46:20 -07003#include <crow/app.h>
Ed Tanous911ac312017-08-15 09:37:42 -07004
5#include <dbus/connection.hpp>
6#include <dbus/endpoint.hpp>
7#include <dbus/filter.hpp>
8#include <dbus/match.hpp>
9#include <dbus/message.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -070010#include <persistent_data_middleware.hpp>
11#include <token_authorization_middleware.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070012#include <fstream>
Ed Tanousba9f9a62017-10-11 16:40:35 -070013#include <streambuf>
14#include <string>
Ed Tanous911ac312017-08-15 09:37:42 -070015
Ed Tanous3dac7492017-08-02 13:46:20 -070016namespace crow {
17namespace redfish {
18
19template <typename... Middlewares>
Ed Tanousba9f9a62017-10-11 16:40:35 -070020void get_redfish_sub_routes(Crow<Middlewares...>& app, const std::string& url,
21 nlohmann::json& j) {
22 auto routes = app.get_routes(url);
23 for (auto& route : routes) {
24 auto redfish_sub_route =
25 route.substr(url.size(), route.size() - url.size() - 1);
26 // check if the route is at this level, and we didn't find and exact match
27 // also, filter out resources that start with $ to remove $metadata
28 if (!redfish_sub_route.empty() && redfish_sub_route[0] != '$' &&
29 redfish_sub_route.find('/') == std::string::npos) {
30 j[redfish_sub_route] = nlohmann::json{{"@odata.id", route}};
Ed Tanous911ac312017-08-15 09:37:42 -070031 }
Ed Tanousba9f9a62017-10-11 16:40:35 -070032 }
33}
Ed Tanous911ac312017-08-15 09:37:42 -070034
Ed Tanousba9f9a62017-10-11 16:40:35 -070035template <typename... Middlewares>
36void request_routes(Crow<Middlewares...>& app) {
37 CROW_ROUTE(app, "/redfish/")
Ed Tanous911ac312017-08-15 09:37:42 -070038 .methods("GET"_method)([](const crow::request& req, crow::response& res) {
Ed Tanousba9f9a62017-10-11 16:40:35 -070039 res.json_value = {{"v1", "/redfish/v1/"}};
40 res.end();
41 });
42
43 CROW_ROUTE(app, "/redfish/v1/")
44 .methods(
45 "GET"_method)([&](const crow::request& req, crow::response& res) {
46 res.json_value = {
47 {"@odata.context", "/redfish/v1/$metadata#ServiceRoot.ServiceRoot"},
48 {"@odata.id", "/redfish/v1/"},
49 {"@odata.type", "#ServiceRoot.v1_1_1.ServiceRoot"},
50 {"Id", "RootService"},
51 {"Name", "Root Service"},
52 {"RedfishVersion", "1.1.0"},
53 {"Links",
54 {{"Sessions",
55 {{"@odata.id", "/redfish/v1/SessionService/Sessions/"}}}}}};
56
57 res.json_value["UUID"] =
58 app.template get_middleware<PersistentData::Middleware>()
59 .system_uuid;
60 get_redfish_sub_routes(app, "/redfish/v1/", res.json_value);
61 res.end();
62 });
63
Ed Tanousba9f9a62017-10-11 16:40:35 -070064 CROW_ROUTE(app, "/redfish/v1/Chassis/")
65 .methods("GET"_method)(
66 [&](const crow::request& req, crow::response& res) {
67 std::vector<std::string> entities;
68 /*std::ifstream f("~/system.json");
69
70 nlohmann::json input = nlohmann::json::parse(f);
71 for (auto it = input.begin(); it != input.end(); it++) {
72 auto value = it.value();
73 if (value["type"] == "Chassis") {
74 std::string str = value["name"];
75 entities.emplace_back(str);
76 }
77 }
78 */
79 res.json_value = {
80 {"@odata.context",
81 "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"},
82 {"@odata.id", "/redfish/v1/Chassis"},
83 {"@odata.type", "#ChassisCollection.ChassisCollection"},
84 {"Name", "Chassis Collection"},
85 {"Members@odata.count", entities.size()}};
86
87 get_redfish_sub_routes(app, "/redfish/v1/Chassis", res.json_value);
88 res.end();
89 });
90
91 CROW_ROUTE(app, "/redfish/v1/AccountService/")
92 .methods(
93 "GET"_method)([&](const crow::request& req, crow::response& res) {
94 res.json_value = {
95 {"@odata.context",
96 "/redfish/v1/$metadata#AccountService.AccountService"},
97 {"@odata.id", "/redfish/v1/AccountService"},
98 {"@odata.type", "#AccountService.v1_1_0.AccountService"},
99 {"Id", "AccountService"},
100 {"Name", "Account Service"},
101 {"Description", "BMC User Accounts"},
102 {"Status",
103 // TODO(ed) health rollup
104 {{"State", "Enabled"}, {"Health", "OK"}, {"HealthRollup", "OK"}}},
105 {"ServiceEnabled", true},
106 {"MinPasswordLength", 1},
107 {"MaxPasswordLength", 20},
108 };
109 get_redfish_sub_routes(app, "/redfish/v1/AccountService",
110 res.json_value);
111 res.end();
112 });
113
114 CROW_ROUTE(app, "/redfish/v1/AccountService/Roles/")
115 .methods("GET"_method)(
116 [&](const crow::request& req, crow::response& res) {
117 res.json_value = {
118 {"@odata.context",
119 "/redfish/v1/$metadata#RoleCollection.RoleCollection"},
120 {"@odata.id", "/redfish/v1/AccountService/Roles"},
121 {"@odata.type", "#RoleCollection.RoleCollection"},
122 {"Name", "Account Service"},
123 {"Description", "BMC User Roles"},
124 {"Members@odata.count", 1},
125 {"Members",
126 {{"@odata.id",
127 "/redfish/v1/AccountService/Roles/Administrator"}}}};
128 get_redfish_sub_routes(app, "/redfish/v1/AccountService",
129 res.json_value);
130 res.end();
131 });
132
133 CROW_ROUTE(app, "/redfish/v1/AccountService/Roles/Administrator/")
134 .methods("GET"_method)(
135 [&](const crow::request& req, crow::response& res) {
136 res.json_value = {
137 {"@odata.context", "/redfish/v1/$metadata#Role.Role"},
138 {"@odata.id", "/redfish/v1/AccountService/Roles/Administrator"},
139 {"@odata.type", "#Role.v1_0_2.Role"},
140 {"Id", "Administrator"},
141 {"Name", "User Role"},
142 {"Description", "Administrator User Role"},
143 {"IsPredefined", true},
144 {"AssignedPrivileges",
145 {"Login", "ConfigureManager", "ConfigureUsers",
146 "ConfigureSelf", "ConfigureComponents"}}};
147 res.end();
148 });
149
150 CROW_ROUTE(app, "/redfish/v1/AccountService/Accounts/")
151 .methods(
152 "GET"_method)([&](const crow::request& req, crow::response& res) {
Ed Tanous911ac312017-08-15 09:37:42 -0700153 boost::asio::io_service io;
154 auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
155 dbus::endpoint user_list("org.openbmc.UserManager",
156 "/org/openbmc/UserManager/Users",
157 "org.openbmc.Enrol", "UserList");
158 bus->async_method_call(
159 [&](const boost::system::error_code ec,
Ed Tanousba9f9a62017-10-11 16:40:35 -0700160 const std::vector<std::string>& users) {
Ed Tanous911ac312017-08-15 09:37:42 -0700161 if (ec) {
162 res.code = 500;
163 } else {
Ed Tanousba9f9a62017-10-11 16:40:35 -0700164 res.json_value = {
Ed Tanous911ac312017-08-15 09:37:42 -0700165 {"@odata.context",
166 "/redfish/v1/"
167 "$metadata#ManagerAccountCollection."
168 "ManagerAccountCollection"},
169 {"@odata.id", "/redfish/v1/AccountService/Accounts"},
170 {"@odata.type",
171 "#ManagerAccountCollection.ManagerAccountCollection"},
172 {"Name", "Accounts Collection"},
173 {"Description", "BMC User Accounts"},
174 {"Members@odata.count", users.size()}};
Ed Tanousba9f9a62017-10-11 16:40:35 -0700175 nlohmann::json member_array = nlohmann::json::array();
Ed Tanous911ac312017-08-15 09:37:42 -0700176 int user_index = 0;
177 for (auto& user : users) {
178 member_array.push_back(
Ed Tanousba9f9a62017-10-11 16:40:35 -0700179 {{"@odata.id",
180 "/redfish/v1/AccountService/Accounts/" +
181 std::to_string(++user_index)}});
Ed Tanous911ac312017-08-15 09:37:42 -0700182 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700183 res.json_value["Members"] = member_array;
Ed Tanous911ac312017-08-15 09:37:42 -0700184 }
185 res.end();
Ed Tanousba9f9a62017-10-11 16:40:35 -0700186 },
187 user_list);
Ed Tanous3dac7492017-08-02 13:46:20 -0700188 });
189
190 CROW_ROUTE(app, "/redfish/v1/AccountService/Accounts/<int>/")
Ed Tanousba9f9a62017-10-11 16:40:35 -0700191 .methods("GET"_method)([](const crow::request& req, crow::response& res,
192 int account_index) {
193 res.json_value = {
Ed Tanous3dac7492017-08-02 13:46:20 -0700194 {"@odata.context",
195 "/redfish/v1/$metadata#ManagerAccount.ManagerAccount"},
196 {"@odata.id", "/redfish/v1/AccountService/Accounts/1"},
197 {"@odata.type", "#ManagerAccount.v1_0_3.ManagerAccount"},
198 {"Id", "1"},
199 {"Name", "User Account"},
200 {"Description", "User Account"},
201 {"Enabled", false},
202 {"Password", nullptr},
203 {"UserName", "anonymous"},
204 {"RoleId", "NoAccess"},
205 {"Links",
206 {{"Role",
207 {{"@odata.id", "/redfish/v1/AccountService/Roles/NoAccess"}}}}}};
Ed Tanousba9f9a62017-10-11 16:40:35 -0700208 res.end();
209 });
210
211 CROW_ROUTE(app, "/redfish/v1/SessionService/")
212 .methods(
213 "GET"_method)([&](const crow::request& req, crow::response& res) {
214 res.json_value = {
215 {"@odata.context",
216 "/redfish/v1/$metadata#SessionService.SessionService"},
217 {"@odata.id", "/redfish/v1/SessionService"},
218 {"@odata.type", "#SessionService.v1_1_1.SessionService"},
219 {"Id", "SessionService"},
220 {"Name", "SessionService"},
221 {"Description", "SessionService"},
222 {"Status",
223 {{"State", "Enabled"}, {"Health", "OK"}, {"HealthRollup", "OK"}}},
224 {"ServiceEnabled", true},
225 // TODO(ed) converge with session timeouts once they exist
226 // Bogus number for now
227 {"SessionTimeout", 1800}};
228 get_redfish_sub_routes(app, "/redfish/v1/AccountService",
229 res.json_value);
230 res.end();
231 });
232
233 CROW_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
234 .methods("POST"_method, "GET"_method)([&](const crow::request& req,
235 crow::response& res) {
236 auto& data_middleware =
237 app.template get_middleware<PersistentData::Middleware>();
238 if (req.method == "POST"_method) {
239 // call with exceptions disabled
240 auto login_credentials =
241 nlohmann::json::parse(req.body, nullptr, false);
242 if (login_credentials.is_discarded()) {
243 res.code = 400;
244 res.end();
245 return;
246 }
247 // check for username/password in the root object
248 auto user_it = login_credentials.find("UserName");
249 auto pass_it = login_credentials.find("Password");
250 if (user_it == login_credentials.end() ||
251 pass_it == login_credentials.end()) {
252 res.code = 400;
253 res.end();
254 return;
255 }
256
257 std::string username = user_it->get<const std::string>();
258 std::string password = pass_it->get<const std::string>();
259 if (username.empty() || password.empty()) {
260 res.code = 400;
261 res.end();
262 return;
263 }
264
265 if (!pam_authenticate_user(username, password)) {
266 res.code = 401;
267 res.end();
268 return;
269 }
270 auto session = data_middleware.generate_user_session(username);
271 res.code = 200;
272 res.add_header("X-Auth-Token", session.session_token);
273 res.json_value = {
274 {"@odata.context", "/redfish/v1/$metadata#Session"},
275 {"@odata.id",
276 "/redfish/v1/SessionService/Sessions/" + session.unique_id},
277 {"@odata.type", "#Session.v1_0_3.Session"},
278 {"Id", session.unique_id},
279 {"Name", "User Session"},
280 {"Description", "Manager User Session"},
281 {"UserName", username}};
282 } else { // assume get
283 res.json_value = {
284 {"@odata.context",
285 "/redfish/v1/$metadata#SessionCollection.SessionCollection"},
286 {"@odata.id", "/redfish/v1/SessionService/Sessions"},
287 {"@odata.type", "#SessionCollection.SessionCollection"},
288 {"Name", "Session Collection"},
289 {"Description", "Session Collection"},
290 {"Members@odata.count", data_middleware.auth_tokens.size()}
291
292 };
293 nlohmann::json member_array = nlohmann::json::array();
294 for (auto& session : data_middleware.auth_tokens) {
295 member_array.push_back({{"@odata.id",
296 "/redfish/v1/SessionService/Sessions/" +
297 session.second.unique_id}});
298 }
299 res.json_value["Members"] = member_array;
300 }
301 res.end();
302 });
303
304 CROW_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
305 .methods("GET"_method, "DELETE"_method)([&](const crow::request& req,
306 crow::response& res,
307 const std::string& session) {
308 auto& data_middleware =
309 app.template get_middleware<PersistentData::Middleware>();
Ed Tanous710adfc2017-10-24 17:04:52 -0700310 // TODO(Ed) this is inefficient
311 auto session_it = data_middleware.auth_tokens.begin();
312 while (session_it != data_middleware.auth_tokens.end()) {
313 if (session_it->second.unique_id == session) {
314 break;
315 }
316 session_it++;
317 }
318
Ed Tanousba9f9a62017-10-11 16:40:35 -0700319 if (session_it == data_middleware.auth_tokens.end()) {
320 res.code = 404;
321 res.end();
322 return;
323 }
324 if (req.method == "DELETE"_method) {
325 data_middleware.auth_tokens.erase(session_it);
326 res.code = 200;
327 } else { // assume get
328 res.json_value = {
329 {"@odata.context", "/redfish/v1/$metadata#Session.Session"},
330 {"@odata.id", "/redfish/v1/SessionService/Sessions/" + session},
331 {"@odata.type", "#Session.v1_0_3.Session"},
332 {"Id", session_it->second.unique_id},
333 {"Name", "User Session"},
334 {"Description", "Manager User Session"},
335 {"UserName", session_it->second.username}};
336 }
337 res.end();
Ed Tanous3dac7492017-08-02 13:46:20 -0700338 });
339}
Ed Tanous911ac312017-08-15 09:37:42 -0700340} // namespace redfish
341} // namespace crow