blob: b3b4e7af7189ff225dbaa09f547c8f24e9abffde [file] [log] [blame]
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +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
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010017
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080018#include "app.hpp"
Kowalski, Kamilf4c4dcf2018-01-29 14:55:35 +010019#include "error_messages.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080020#include "http/utility.hpp"
Ed Tanous52cc1122020-07-18 13:51:21 -070021#include "persistent_data.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080022#include "query.hpp"
23#include "registries/privilege_registry.hpp"
24#include "utils/json_utils.hpp"
John Edward Broadbent7e860f12021-04-08 15:57:16 -070025
Ed Tanous1abe55e2018-09-05 08:30:59 -070026namespace redfish
27{
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010028
Ed Tanous4f48d5f2021-06-21 08:27:45 -070029inline void fillSessionObject(crow::Response& res,
30 const persistent_data::UserSession& session)
Ed Tanous1abe55e2018-09-05 08:30:59 -070031{
Ed Tanousfaa34cc2021-06-03 13:27:02 -070032 res.jsonValue["Id"] = session.uniqueId;
33 res.jsonValue["UserName"] = session.username;
34 res.jsonValue["@odata.id"] =
35 "/redfish/v1/SessionService/Sessions/" + session.uniqueId;
Ed Tanousbb759e32022-08-02 17:07:54 -070036 res.jsonValue["@odata.type"] = "#Session.v1_5_0.Session";
Ed Tanousfaa34cc2021-06-03 13:27:02 -070037 res.jsonValue["Name"] = "User Session";
38 res.jsonValue["Description"] = "Manager User Session";
39 res.jsonValue["ClientOriginIPAddress"] = session.clientIp;
Ed Tanousbb759e32022-08-02 17:07:54 -070040 if (session.clientId)
41 {
42 res.jsonValue["Context"] = *session.clientId;
43 }
44// The below implementation is deprecated in leiu of Session.Context
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -050045#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
Ed Tanousfaa34cc2021-06-03 13:27:02 -070046 res.jsonValue["Oem"]["OpenBMC"]["@odata.type"] =
47 "#OemSession.v1_0_0.Session";
Ed Tanousbb759e32022-08-02 17:07:54 -070048 res.jsonValue["Oem"]["OpenBMC"]["ClientID"] = session.clientId.value_or("");
Sunitha Harish08bdcc72020-05-12 05:17:57 -050049#endif
Ed Tanousfaa34cc2021-06-03 13:27:02 -070050}
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010051
Ed Tanous724340d2022-03-14 09:10:07 -070052inline void
Ed Tanousa1e08712022-07-07 16:10:39 -070053 handleSessionHead(crow::App& app, const crow::Request& req,
54 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
55 const std::string& /*sessionId*/)
Ed Tanous724340d2022-03-14 09:10:07 -070056{
Ed Tanousa1e08712022-07-07 16:10:39 -070057
Carson Labrado3ba00072022-06-06 19:40:56 +000058 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous45ca1b82022-03-25 13:07:27 -070059 {
60 return;
61 }
Ed Tanousa1e08712022-07-07 16:10:39 -070062 asyncResp->res.addHeader(
63 boost::beast::http::field::link,
64 "</redfish/v1/JsonSchemas/Session/Session.json>; rel=describedby");
65}
66
67inline void
68 handleSessionGet(crow::App& app, const crow::Request& req,
69 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
70 const std::string& sessionId)
71{
72 handleSessionHead(app, req, asyncResp, sessionId);
73
Ed Tanous724340d2022-03-14 09:10:07 -070074 // Note that control also reaches here via doPost and doDelete.
75 auto session =
76 persistent_data::SessionStore::getInstance().getSessionByUid(sessionId);
77
78 if (session == nullptr)
79 {
80 messages::resourceNotFound(asyncResp->res, "Session", sessionId);
81 return;
82 }
83
84 fillSessionObject(asyncResp->res, *session);
85}
86
87inline void
Ed Tanous45ca1b82022-03-25 13:07:27 -070088 handleSessionDelete(crow::App& app, const crow::Request& req,
Ed Tanous724340d2022-03-14 09:10:07 -070089 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
90 const std::string& sessionId)
91{
Carson Labrado3ba00072022-06-06 19:40:56 +000092 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous45ca1b82022-03-25 13:07:27 -070093 {
94 return;
95 }
Ed Tanous724340d2022-03-14 09:10:07 -070096 auto session =
97 persistent_data::SessionStore::getInstance().getSessionByUid(sessionId);
98
99 if (session == nullptr)
100 {
101 messages::resourceNotFound(asyncResp->res, "Session", sessionId);
102 return;
103 }
104
105 // Perform a proper ConfigureSelf authority check. If a
106 // session is being used to DELETE some other user's session,
107 // then the ConfigureSelf privilege does not apply. In that
108 // case, perform the authority check again without the user's
109 // ConfigureSelf privilege.
wukaihua-fii-na0fd29862022-05-18 09:19:16 +0800110 if (req.session != nullptr && !session->username.empty() &&
111 session->username != req.session->username)
Ed Tanous724340d2022-03-14 09:10:07 -0700112 {
113 Privileges effectiveUserPrivileges =
114 redfish::getUserPrivileges(req.userRole);
115
116 if (!effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"}))
117 {
118 messages::insufficientPrivilege(asyncResp->res);
119 return;
120 }
121 }
122
123 persistent_data::SessionStore::getInstance().removeSession(session);
124 messages::success(asyncResp->res);
125}
126
127inline nlohmann::json getSessionCollectionMembers()
128{
129 std::vector<const std::string*> sessionIds =
130 persistent_data::SessionStore::getInstance().getUniqueIds(
131 false, persistent_data::PersistenceType::TIMEOUT);
132 nlohmann::json ret = nlohmann::json::array();
133 for (const std::string* uid : sessionIds)
134 {
Ed Tanous14766872022-03-15 10:44:42 -0700135 nlohmann::json::object_t session;
136 session["@odata.id"] = "/redfish/v1/SessionService/Sessions/" + *uid;
137 ret.push_back(std::move(session));
Ed Tanous724340d2022-03-14 09:10:07 -0700138 }
139 return ret;
140}
141
Ed Tanousa1e08712022-07-07 16:10:39 -0700142inline void handleSessionCollectionHead(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700143 crow::App& app, const crow::Request& req,
Ed Tanous724340d2022-03-14 09:10:07 -0700144 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
145{
Ed Tanousa1e08712022-07-07 16:10:39 -0700146
Carson Labrado3ba00072022-06-06 19:40:56 +0000147 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous45ca1b82022-03-25 13:07:27 -0700148 {
149 return;
150 }
Ed Tanousa1e08712022-07-07 16:10:39 -0700151 asyncResp->res.addHeader(
152 boost::beast::http::field::link,
153 "</redfish/v1/JsonSchemas/SessionCollection.json>; rel=describedby");
154}
155
156inline void handleSessionCollectionGet(
157 crow::App& app, const crow::Request& req,
158 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
159{
160 handleSessionCollectionHead(app, req, asyncResp);
Ed Tanous724340d2022-03-14 09:10:07 -0700161 asyncResp->res.jsonValue["Members"] = getSessionCollectionMembers();
162 asyncResp->res.jsonValue["Members@odata.count"] =
163 asyncResp->res.jsonValue["Members"].size();
164 asyncResp->res.jsonValue["@odata.type"] =
165 "#SessionCollection.SessionCollection";
166 asyncResp->res.jsonValue["@odata.id"] =
167 "/redfish/v1/SessionService/Sessions/";
168 asyncResp->res.jsonValue["Name"] = "Session Collection";
169 asyncResp->res.jsonValue["Description"] = "Session Collection";
170}
171
172inline void handleSessionCollectionMembersGet(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700173 crow::App& app, const crow::Request& req,
Ed Tanous724340d2022-03-14 09:10:07 -0700174 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
175{
Carson Labrado3ba00072022-06-06 19:40:56 +0000176 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous45ca1b82022-03-25 13:07:27 -0700177 {
178 return;
179 }
Ed Tanous724340d2022-03-14 09:10:07 -0700180 asyncResp->res.jsonValue = getSessionCollectionMembers();
181}
182
Ed Tanous4ee8e212022-05-28 09:42:51 -0700183inline void handleSessionCollectionPost(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700184 crow::App& app, const crow::Request& req,
Ed Tanous724340d2022-03-14 09:10:07 -0700185 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
186{
Carson Labrado3ba00072022-06-06 19:40:56 +0000187 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous45ca1b82022-03-25 13:07:27 -0700188 {
189 return;
190 }
Ed Tanous724340d2022-03-14 09:10:07 -0700191 std::string username;
192 std::string password;
193 std::optional<nlohmann::json> oemObject;
Ed Tanousbb759e32022-08-02 17:07:54 -0700194 std::optional<std::string> clientId;
Ed Tanous724340d2022-03-14 09:10:07 -0700195 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", username,
Ed Tanousbb759e32022-08-02 17:07:54 -0700196 "Password", password, "Context", clientId,
197 "Oem", oemObject))
Ed Tanous724340d2022-03-14 09:10:07 -0700198 {
199 return;
200 }
201
202 if (password.empty() || username.empty() ||
203 asyncResp->res.result() != boost::beast::http::status::ok)
204 {
205 if (username.empty())
206 {
207 messages::propertyMissing(asyncResp->res, "UserName");
208 }
209
210 if (password.empty())
211 {
212 messages::propertyMissing(asyncResp->res, "Password");
213 }
214
215 return;
216 }
217
218 int pamrc = pamAuthenticateUser(username, password);
219 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
220 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
221 {
222 messages::resourceAtUriUnauthorized(asyncResp->res, req.urlView,
223 "Invalid username or password");
224 return;
225 }
226#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
227 if (oemObject)
228 {
229 std::optional<nlohmann::json> bmcOem;
230 if (!json_util::readJson(*oemObject, asyncResp->res, "OpenBMC", bmcOem))
231 {
232 return;
233 }
Ed Tanousbb759e32022-08-02 17:07:54 -0700234
235 std::optional<std::string> oemClientId;
236 if (!json_util::readJson(*bmcOem, asyncResp->res, "ClientID",
237 oemClientId))
Ed Tanous724340d2022-03-14 09:10:07 -0700238 {
239 BMCWEB_LOG_ERROR << "Could not read ClientId";
240 return;
241 }
Ed Tanousbb759e32022-08-02 17:07:54 -0700242 if (oemClientId)
243 {
244 if (clientId)
245 {
246 messages::propertyValueConflict(*oemClientId, *clientId);
247 return;
248 }
249 clientId = *oemClientId;
250 }
Ed Tanous724340d2022-03-14 09:10:07 -0700251 }
252#endif
253
254 // User is authenticated - create session
255 std::shared_ptr<persistent_data::UserSession> session =
256 persistent_data::SessionStore::getInstance().generateUserSession(
257 username, req.ipAddress, clientId,
258 persistent_data::PersistenceType::TIMEOUT, isConfigureSelfOnly);
Brad Bishop02e53ae2022-07-29 14:38:40 -0400259 if (session == nullptr)
260 {
261 messages::internalError(asyncResp->res);
262 return;
263 }
264
Ed Tanous724340d2022-03-14 09:10:07 -0700265 asyncResp->res.addHeader("X-Auth-Token", session->sessionToken);
266 asyncResp->res.addHeader(
267 "Location", "/redfish/v1/SessionService/Sessions/" + session->uniqueId);
268 asyncResp->res.result(boost::beast::http::status::created);
269 if (session->isConfigureSelfOnly)
270 {
271 messages::passwordChangeRequired(
272 asyncResp->res,
273 crow::utility::urlFromPieces("redfish", "v1", "AccountService",
Brad Bishop85e64712022-07-29 12:59:07 -0400274 "Accounts", session->username));
Ed Tanous724340d2022-03-14 09:10:07 -0700275 }
276
277 fillSessionObject(asyncResp->res, *session);
278}
Ed Tanousa1e08712022-07-07 16:10:39 -0700279inline void handleSessionServiceHead(
280 crow::App& app, const crow::Request& req,
281 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
282{
283
284 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
285 {
286 return;
287 }
288 asyncResp->res.addHeader(
289 boost::beast::http::field::link,
290 "</redfish/v1/JsonSchemas/SessionService/SessionService.json>; rel=describedby");
291}
Ed Tanous724340d2022-03-14 09:10:07 -0700292inline void
Ed Tanous45ca1b82022-03-25 13:07:27 -0700293 handleSessionServiceGet(crow::App& app, const crow::Request& req,
Ed Tanous724340d2022-03-14 09:10:07 -0700294 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
295
296{
Ed Tanousa1e08712022-07-07 16:10:39 -0700297 handleSessionServiceHead(app, req, asyncResp);
Ed Tanous724340d2022-03-14 09:10:07 -0700298 asyncResp->res.jsonValue["@odata.type"] =
299 "#SessionService.v1_0_2.SessionService";
300 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/";
301 asyncResp->res.jsonValue["Name"] = "Session Service";
302 asyncResp->res.jsonValue["Id"] = "SessionService";
303 asyncResp->res.jsonValue["Description"] = "Session Service";
304 asyncResp->res.jsonValue["SessionTimeout"] =
305 persistent_data::SessionStore::getInstance().getTimeoutInSeconds();
306 asyncResp->res.jsonValue["ServiceEnabled"] = true;
307
Ed Tanous14766872022-03-15 10:44:42 -0700308 asyncResp->res.jsonValue["Sessions"]["@odata.id"] =
309 "/redfish/v1/SessionService/Sessions";
Ed Tanous724340d2022-03-14 09:10:07 -0700310}
311
312inline void handleSessionServicePatch(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700313 crow::App& app, const crow::Request& req,
Ed Tanous724340d2022-03-14 09:10:07 -0700314 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
315{
Carson Labrado3ba00072022-06-06 19:40:56 +0000316 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous45ca1b82022-03-25 13:07:27 -0700317 {
318 return;
319 }
Ed Tanous724340d2022-03-14 09:10:07 -0700320 std::optional<int64_t> sessionTimeout;
321 if (!json_util::readJsonPatch(req, asyncResp->res, "SessionTimeout",
322 sessionTimeout))
323 {
324 return;
325 }
326
327 if (sessionTimeout)
328 {
329 // The mininum & maximum allowed values for session timeout
330 // are 30 seconds and 86400 seconds respectively as per the
331 // session service schema mentioned at
332 // https://redfish.dmtf.org/schemas/v1/SessionService.v1_1_7.json
333
334 if (*sessionTimeout <= 86400 && *sessionTimeout >= 30)
335 {
336 std::chrono::seconds sessionTimeoutInseconds(*sessionTimeout);
337 persistent_data::SessionStore::getInstance().updateSessionTimeout(
338 sessionTimeoutInseconds);
339 messages::propertyValueModified(asyncResp->res, "SessionTimeOut",
340 std::to_string(*sessionTimeout));
341 }
342 else
343 {
344 messages::propertyValueNotInList(asyncResp->res,
345 std::to_string(*sessionTimeout),
346 "SessionTimeOut");
347 }
348 }
349}
350
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700351inline void requestRoutesSession(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700352{
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700353 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
Ed Tanousa1e08712022-07-07 16:10:39 -0700354 .privileges(redfish::privileges::headSession)
355 .methods(boost::beast::http::verb::head)(
356 std::bind_front(handleSessionHead, std::ref(app)));
357
358 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700359 .privileges(redfish::privileges::getSession)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700360 .methods(boost::beast::http::verb::get)(
361 std::bind_front(handleSessionGet, std::ref(app)));
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100362
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700363 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700364 .privileges(redfish::privileges::deleteSession)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700365 .methods(boost::beast::http::verb::delete_)(
366 std::bind_front(handleSessionDelete, std::ref(app)));
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700367
368 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
Ed Tanousa1e08712022-07-07 16:10:39 -0700369 .privileges(redfish::privileges::headSessionCollection)
370 .methods(boost::beast::http::verb::head)(
371 std::bind_front(handleSessionCollectionHead, std::ref(app)));
372
373 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
Ed Tanoused398212021-06-09 17:05:54 -0700374 .privileges(redfish::privileges::getSessionCollection)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700375 .methods(boost::beast::http::verb::get)(
376 std::bind_front(handleSessionCollectionGet, std::ref(app)));
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700377
Ed Tanouse76cd862022-03-14 09:12:00 -0700378 // Note, the next two routes technically don't match the privilege
Ed Tanous724340d2022-03-14 09:10:07 -0700379 // registry given the way login mechanisms work. The base privilege
380 // registry lists this endpoint as requiring login privilege, but because
381 // this is the endpoint responsible for giving the login privilege, and it
382 // is itself its own route, it needs to not require Login
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700383 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
384 .privileges({})
Ed Tanous45ca1b82022-03-25 13:07:27 -0700385 .methods(boost::beast::http::verb::post)(
386 std::bind_front(handleSessionCollectionPost, std::ref(app)));
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100387
Ed Tanouse76cd862022-03-14 09:12:00 -0700388 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/Members/")
389 .privileges({})
Ed Tanous45ca1b82022-03-25 13:07:27 -0700390 .methods(boost::beast::http::verb::post)(
391 std::bind_front(handleSessionCollectionPost, std::ref(app)));
Ed Tanouse76cd862022-03-14 09:12:00 -0700392
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700393 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
Ed Tanousa1e08712022-07-07 16:10:39 -0700394 .privileges(redfish::privileges::headSessionService)
395 .methods(boost::beast::http::verb::head)(
396 std::bind_front(handleSessionServiceHead, std::ref(app)));
397
398 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
Ed Tanoused398212021-06-09 17:05:54 -0700399 .privileges(redfish::privileges::getSessionService)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700400 .methods(boost::beast::http::verb::get)(
401 std::bind_front(handleSessionServiceGet, std::ref(app)));
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100402
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700403 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
Ed Tanoused398212021-06-09 17:05:54 -0700404 .privileges(redfish::privileges::patchSessionService)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700405 .methods(boost::beast::http::verb::patch)(
406 std::bind_front(handleSessionServicePatch, std::ref(app)));
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700407}
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100408
Ed Tanous1abe55e2018-09-05 08:30:59 -0700409} // namespace redfish