blob: 1568c0022d0e0c22f3af9b6fb331761c0cef8aa0 [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
Kowalski, Kamilf4c4dcf2018-01-29 14:55:35 +010018#include "error_messages.hpp"
Ed Tanous52cc1122020-07-18 13:51:21 -070019#include "persistent_data.hpp"
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010020
John Edward Broadbent7e860f12021-04-08 15:57:16 -070021#include <app.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070022#include <registries/privilege_registry.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070023
Ed Tanous1abe55e2018-09-05 08:30:59 -070024namespace redfish
25{
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010026
27class SessionCollection;
28
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;
36 res.jsonValue["@odata.type"] = "#Session.v1_3_0.Session";
37 res.jsonValue["Name"] = "User Session";
38 res.jsonValue["Description"] = "Manager User Session";
39 res.jsonValue["ClientOriginIPAddress"] = session.clientIp;
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -050040#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
Ed Tanousfaa34cc2021-06-03 13:27:02 -070041 res.jsonValue["Oem"]["OpenBMC"]["@odata.type"] =
42 "#OemSession.v1_0_0.Session";
43 res.jsonValue["Oem"]["OpenBMC"]["ClientID"] = session.clientId;
Sunitha Harish08bdcc72020-05-12 05:17:57 -050044#endif
Ed Tanousfaa34cc2021-06-03 13:27:02 -070045}
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010046
Ed Tanousfaa34cc2021-06-03 13:27:02 -070047inline void requestRoutesSession(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -070048{
Ed Tanousfaa34cc2021-06-03 13:27:02 -070049 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -070050 .privileges(redfish::privileges::getSession)
Ed Tanousfaa34cc2021-06-03 13:27:02 -070051 .methods(boost::beast::http::verb::get)(
52 [](const crow::Request& /*req*/,
53 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
54 const std::string& sessionId) -> void {
55 // Note that control also reaches here via doPost and doDelete.
56 auto session = persistent_data::SessionStore::getInstance()
57 .getSessionByUid(sessionId);
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010058
Ed Tanousfaa34cc2021-06-03 13:27:02 -070059 if (session == nullptr)
60 {
61 messages::resourceNotFound(asyncResp->res, "Session",
62 sessionId);
63 return;
64 }
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010065
Ed Tanousfaa34cc2021-06-03 13:27:02 -070066 fillSessionObject(asyncResp->res, *session);
67 });
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010068
Ed Tanousfaa34cc2021-06-03 13:27:02 -070069 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -070070 .privileges(redfish::privileges::deleteSession)
Ed Tanousfaa34cc2021-06-03 13:27:02 -070071 .methods(boost::beast::http::verb::delete_)(
72 [](const crow::Request& req,
73 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
74 const std::string& sessionId) -> void {
75 auto session = persistent_data::SessionStore::getInstance()
76 .getSessionByUid(sessionId);
Ed Tanousb9845d92018-07-24 14:38:06 -070077
Ed Tanousfaa34cc2021-06-03 13:27:02 -070078 if (session == nullptr)
79 {
80 messages::resourceNotFound(asyncResp->res, "Session",
81 sessionId);
82 return;
83 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070084
Ed Tanousfaa34cc2021-06-03 13:27:02 -070085 // Perform a proper ConfigureSelf authority check. If a
86 // session is being used to DELETE some other user's session,
87 // then the ConfigureSelf privilege does not apply. In that
88 // case, perform the authority check again without the user's
89 // ConfigureSelf privilege.
90 if (session->username != req.session->username)
91 {
92 Privileges effectiveUserPrivileges =
93 redfish::getUserPrivileges(req.userRole);
Ed Tanous1abe55e2018-09-05 08:30:59 -070094
Ed Tanousfaa34cc2021-06-03 13:27:02 -070095 if (!effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -070096 {"ConfigureUsers"}))
Ed Tanousfaa34cc2021-06-03 13:27:02 -070097 {
98 messages::insufficientPrivilege(asyncResp->res);
99 return;
100 }
101 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700102
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700103 persistent_data::SessionStore::getInstance().removeSession(
104 session);
Ed Tanous5cc148a2021-06-10 16:26:46 -0700105 messages::success(asyncResp->res);
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700106 });
107
108 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
Ed Tanoused398212021-06-09 17:05:54 -0700109 .privileges(redfish::privileges::getSessionCollection)
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700110 .methods(boost::beast::http::verb::get)(
111 [](const crow::Request& /*req*/,
112 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
113 std::vector<const std::string*> sessionIds =
114 persistent_data::SessionStore::getInstance().getUniqueIds(
115 false, persistent_data::PersistenceType::TIMEOUT);
116
117 asyncResp->res.jsonValue["Members@odata.count"] =
118 sessionIds.size();
119 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
120 for (const std::string* uid : sessionIds)
121 {
122 asyncResp->res.jsonValue["Members"].push_back(
123 {{"@odata.id",
124 "/redfish/v1/SessionService/Sessions/" + *uid}});
125 }
126 asyncResp->res.jsonValue["Members@odata.count"] =
127 sessionIds.size();
128 asyncResp->res.jsonValue["@odata.type"] =
129 "#SessionCollection.SessionCollection";
130 asyncResp->res.jsonValue["@odata.id"] =
131 "/redfish/v1/SessionService/Sessions/";
132 asyncResp->res.jsonValue["Name"] = "Session Collection";
133 asyncResp->res.jsonValue["Description"] = "Session Collection";
134 });
135
136 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
Ed Tanoused398212021-06-09 17:05:54 -0700137 // Note, this technically doesn't match the privilege registry given the
138 // way login mechanisms work. The base privilege registry lists this
139 // endpoint as requiring login privilege, but because this is the
140 // endpoint responsible for giving the login privilege, and it is itself
141 // its own route, it needs to not require Login
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700142 .privileges({})
143 .methods(boost::beast::http::verb::post)(
144 [](const crow::Request& req,
145 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
146 std::string username;
147 std::string password;
148 std::optional<nlohmann::json> oemObject;
149 std::string clientId;
Willy Tu15ed6782021-12-14 11:03:16 -0800150 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
151 username, "Password", password,
152 "Oem", oemObject))
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700153 {
154 return;
155 }
156
157 if (password.empty() || username.empty() ||
158 asyncResp->res.result() != boost::beast::http::status::ok)
159 {
160 if (username.empty())
161 {
162 messages::propertyMissing(asyncResp->res, "UserName");
163 }
164
165 if (password.empty())
166 {
167 messages::propertyMissing(asyncResp->res, "Password");
168 }
169
170 return;
171 }
172
173 int pamrc = pamAuthenticateUser(username, password);
174 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
175 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
176 {
177 messages::resourceAtUriUnauthorized(
178 asyncResp->res, std::string(req.url),
179 "Invalid username or password");
180 return;
181 }
Sunitha Harish08bdcc72020-05-12 05:17:57 -0500182#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700183 if (oemObject)
184 {
185 std::optional<nlohmann::json> bmcOem;
186 if (!json_util::readJson(*oemObject, asyncResp->res,
187 "OpenBMC", bmcOem))
188 {
189 return;
190 }
191 if (!json_util::readJson(*bmcOem, asyncResp->res,
192 "ClientID", clientId))
193 {
194 BMCWEB_LOG_ERROR << "Could not read ClientId";
195 return;
196 }
197 }
Sunitha Harish08bdcc72020-05-12 05:17:57 -0500198#endif
Manojkiran Eda6f115bb2020-06-27 09:52:23 +0530199
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700200 // User is authenticated - create session
201 std::shared_ptr<persistent_data::UserSession> session =
202 persistent_data::SessionStore::getInstance()
203 .generateUserSession(
Jiaqing Zhao41d61c82021-12-07 13:21:47 +0800204 username, req.ipAddress, clientId,
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700205 persistent_data::PersistenceType::TIMEOUT,
206 isConfigureSelfOnly);
207 asyncResp->res.addHeader("X-Auth-Token", session->sessionToken);
208 asyncResp->res.addHeader(
209 "Location",
210 "/redfish/v1/SessionService/Sessions/" + session->uniqueId);
211 asyncResp->res.result(boost::beast::http::status::created);
212 if (session->isConfigureSelfOnly)
213 {
214 messages::passwordChangeRequired(
215 asyncResp->res, "/redfish/v1/AccountService/Accounts/" +
216 session->username);
217 }
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100218
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700219 fillSessionObject(asyncResp->res, *session);
220 });
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100221
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700222 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
Ed Tanoused398212021-06-09 17:05:54 -0700223 .privileges(redfish::privileges::getSessionService)
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700224 .methods(boost::beast::http::verb::get)(
225 [](const crow::Request& /* req */,
226 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
227 asyncResp->res.jsonValue["@odata.type"] =
228 "#SessionService.v1_0_2.SessionService";
229 asyncResp->res.jsonValue["@odata.id"] =
230 "/redfish/v1/SessionService/";
231 asyncResp->res.jsonValue["Name"] = "Session Service";
232 asyncResp->res.jsonValue["Id"] = "SessionService";
233 asyncResp->res.jsonValue["Description"] = "Session Service";
234 asyncResp->res.jsonValue["SessionTimeout"] =
235 persistent_data::SessionStore::getInstance()
236 .getTimeoutInSeconds();
237 asyncResp->res.jsonValue["ServiceEnabled"] = true;
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800238
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700239 asyncResp->res.jsonValue["Sessions"] = {
240 {"@odata.id", "/redfish/v1/SessionService/Sessions"}};
241 });
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100242
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700243 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
Ed Tanoused398212021-06-09 17:05:54 -0700244 .privileges(redfish::privileges::patchSessionService)
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700245 .methods(boost::beast::http::verb::patch)(
246 [](const crow::Request& req,
247 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
248 std::optional<int64_t> sessionTimeout;
Willy Tu15ed6782021-12-14 11:03:16 -0800249 if (!json_util::readJsonPatch(req, asyncResp->res,
250 "SessionTimeout", sessionTimeout))
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700251 {
252 return;
253 }
Ed Tanous0f74e642018-11-12 15:17:05 -0800254
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700255 if (sessionTimeout)
256 {
257 // The mininum & maximum allowed values for session timeout
258 // are 30 seconds and 86400 seconds respectively as per the
259 // session service schema mentioned at
260 // https://redfish.dmtf.org/schemas/v1/SessionService.v1_1_7.json
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530261
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700262 if (*sessionTimeout <= 86400 && *sessionTimeout >= 30)
263 {
264 std::chrono::seconds sessionTimeoutInseconds(
265 *sessionTimeout);
266 persistent_data::SessionStore::getInstance()
267 .updateSessionTimeout(sessionTimeoutInseconds);
268 messages::propertyValueModified(
269 asyncResp->res, "SessionTimeOut",
270 std::to_string(*sessionTimeout));
271 }
272 else
273 {
274 messages::propertyValueNotInList(
275 asyncResp->res, std::to_string(*sessionTimeout),
276 "SessionTimeOut");
277 }
278 }
279 });
280}
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100281
Ed Tanous1abe55e2018-09-05 08:30:59 -0700282} // namespace redfish