blob: a66f460701adb50410fc4af4b0e5b9ac0ed7b433 [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 Tanousace85d62021-10-26 12:45:59 -070022#include <http/utility.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070023#include <registries/privilege_registry.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070024
Ed Tanous1abe55e2018-09-05 08:30:59 -070025namespace redfish
26{
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010027
28class SessionCollection;
29
Ed Tanous4f48d5f2021-06-21 08:27:45 -070030inline void fillSessionObject(crow::Response& res,
31 const persistent_data::UserSession& session)
Ed Tanous1abe55e2018-09-05 08:30:59 -070032{
Ed Tanousfaa34cc2021-06-03 13:27:02 -070033 res.jsonValue["Id"] = session.uniqueId;
34 res.jsonValue["UserName"] = session.username;
35 res.jsonValue["@odata.id"] =
36 "/redfish/v1/SessionService/Sessions/" + session.uniqueId;
37 res.jsonValue["@odata.type"] = "#Session.v1_3_0.Session";
38 res.jsonValue["Name"] = "User Session";
39 res.jsonValue["Description"] = "Manager User Session";
40 res.jsonValue["ClientOriginIPAddress"] = session.clientIp;
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -050041#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
Ed Tanousfaa34cc2021-06-03 13:27:02 -070042 res.jsonValue["Oem"]["OpenBMC"]["@odata.type"] =
43 "#OemSession.v1_0_0.Session";
44 res.jsonValue["Oem"]["OpenBMC"]["ClientID"] = session.clientId;
Sunitha Harish08bdcc72020-05-12 05:17:57 -050045#endif
Ed Tanousfaa34cc2021-06-03 13:27:02 -070046}
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010047
Ed Tanousfaa34cc2021-06-03 13:27:02 -070048inline void requestRoutesSession(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -070049{
Ed Tanousfaa34cc2021-06-03 13:27:02 -070050 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -070051 .privileges(redfish::privileges::getSession)
Ed Tanousfaa34cc2021-06-03 13:27:02 -070052 .methods(boost::beast::http::verb::get)(
53 [](const crow::Request& /*req*/,
54 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
55 const std::string& sessionId) -> void {
56 // Note that control also reaches here via doPost and doDelete.
57 auto session = persistent_data::SessionStore::getInstance()
58 .getSessionByUid(sessionId);
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010059
Ed Tanousfaa34cc2021-06-03 13:27:02 -070060 if (session == nullptr)
61 {
62 messages::resourceNotFound(asyncResp->res, "Session",
63 sessionId);
64 return;
65 }
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010066
Ed Tanousfaa34cc2021-06-03 13:27:02 -070067 fillSessionObject(asyncResp->res, *session);
68 });
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010069
Ed Tanousfaa34cc2021-06-03 13:27:02 -070070 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -070071 .privileges(redfish::privileges::deleteSession)
Ed Tanousfaa34cc2021-06-03 13:27:02 -070072 .methods(boost::beast::http::verb::delete_)(
73 [](const crow::Request& req,
74 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
75 const std::string& sessionId) -> void {
76 auto session = persistent_data::SessionStore::getInstance()
77 .getSessionByUid(sessionId);
Ed Tanousb9845d92018-07-24 14:38:06 -070078
Ed Tanousfaa34cc2021-06-03 13:27:02 -070079 if (session == nullptr)
80 {
81 messages::resourceNotFound(asyncResp->res, "Session",
82 sessionId);
83 return;
84 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070085
Ed Tanousfaa34cc2021-06-03 13:27:02 -070086 // Perform a proper ConfigureSelf authority check. If a
87 // session is being used to DELETE some other user's session,
88 // then the ConfigureSelf privilege does not apply. In that
89 // case, perform the authority check again without the user's
90 // ConfigureSelf privilege.
91 if (session->username != req.session->username)
92 {
93 Privileges effectiveUserPrivileges =
94 redfish::getUserPrivileges(req.userRole);
Ed Tanous1abe55e2018-09-05 08:30:59 -070095
Ed Tanousfaa34cc2021-06-03 13:27:02 -070096 if (!effectiveUserPrivileges.isSupersetOf(
Ed Tanous4f48d5f2021-06-21 08:27:45 -070097 {"ConfigureUsers"}))
Ed Tanousfaa34cc2021-06-03 13:27:02 -070098 {
99 messages::insufficientPrivilege(asyncResp->res);
100 return;
101 }
102 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700104 persistent_data::SessionStore::getInstance().removeSession(
105 session);
Ed Tanous5cc148a2021-06-10 16:26:46 -0700106 messages::success(asyncResp->res);
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700107 });
108
109 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
Ed Tanoused398212021-06-09 17:05:54 -0700110 .privileges(redfish::privileges::getSessionCollection)
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700111 .methods(boost::beast::http::verb::get)(
112 [](const crow::Request& /*req*/,
113 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
114 std::vector<const std::string*> sessionIds =
115 persistent_data::SessionStore::getInstance().getUniqueIds(
116 false, persistent_data::PersistenceType::TIMEOUT);
117
118 asyncResp->res.jsonValue["Members@odata.count"] =
119 sessionIds.size();
120 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
121 for (const std::string* uid : sessionIds)
122 {
123 asyncResp->res.jsonValue["Members"].push_back(
124 {{"@odata.id",
125 "/redfish/v1/SessionService/Sessions/" + *uid}});
126 }
127 asyncResp->res.jsonValue["Members@odata.count"] =
128 sessionIds.size();
129 asyncResp->res.jsonValue["@odata.type"] =
130 "#SessionCollection.SessionCollection";
131 asyncResp->res.jsonValue["@odata.id"] =
132 "/redfish/v1/SessionService/Sessions/";
133 asyncResp->res.jsonValue["Name"] = "Session Collection";
134 asyncResp->res.jsonValue["Description"] = "Session Collection";
135 });
136
137 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
Ed Tanoused398212021-06-09 17:05:54 -0700138 // Note, this technically doesn't match the privilege registry given the
139 // way login mechanisms work. The base privilege registry lists this
140 // endpoint as requiring login privilege, but because this is the
141 // endpoint responsible for giving the login privilege, and it is itself
142 // its own route, it needs to not require Login
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700143 .privileges({})
144 .methods(boost::beast::http::verb::post)(
145 [](const crow::Request& req,
146 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
147 std::string username;
148 std::string password;
149 std::optional<nlohmann::json> oemObject;
150 std::string clientId;
Willy Tu15ed6782021-12-14 11:03:16 -0800151 if (!json_util::readJsonPatch(req, asyncResp->res, "UserName",
152 username, "Password", password,
153 "Oem", oemObject))
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700154 {
155 return;
156 }
157
158 if (password.empty() || username.empty() ||
159 asyncResp->res.result() != boost::beast::http::status::ok)
160 {
161 if (username.empty())
162 {
163 messages::propertyMissing(asyncResp->res, "UserName");
164 }
165
166 if (password.empty())
167 {
168 messages::propertyMissing(asyncResp->res, "Password");
169 }
170
171 return;
172 }
173
174 int pamrc = pamAuthenticateUser(username, password);
175 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
176 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
177 {
178 messages::resourceAtUriUnauthorized(
Ed Tanousace85d62021-10-26 12:45:59 -0700179 asyncResp->res, req.urlView,
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700180 "Invalid username or password");
181 return;
182 }
Sunitha Harish08bdcc72020-05-12 05:17:57 -0500183#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700184 if (oemObject)
185 {
186 std::optional<nlohmann::json> bmcOem;
187 if (!json_util::readJson(*oemObject, asyncResp->res,
188 "OpenBMC", bmcOem))
189 {
190 return;
191 }
192 if (!json_util::readJson(*bmcOem, asyncResp->res,
193 "ClientID", clientId))
194 {
195 BMCWEB_LOG_ERROR << "Could not read ClientId";
196 return;
197 }
198 }
Sunitha Harish08bdcc72020-05-12 05:17:57 -0500199#endif
Manojkiran Eda6f115bb2020-06-27 09:52:23 +0530200
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700201 // User is authenticated - create session
202 std::shared_ptr<persistent_data::UserSession> session =
203 persistent_data::SessionStore::getInstance()
204 .generateUserSession(
Jiaqing Zhao41d61c82021-12-07 13:21:47 +0800205 username, req.ipAddress, clientId,
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700206 persistent_data::PersistenceType::TIMEOUT,
207 isConfigureSelfOnly);
208 asyncResp->res.addHeader("X-Auth-Token", session->sessionToken);
209 asyncResp->res.addHeader(
210 "Location",
211 "/redfish/v1/SessionService/Sessions/" + session->uniqueId);
212 asyncResp->res.result(boost::beast::http::status::created);
213 if (session->isConfigureSelfOnly)
214 {
215 messages::passwordChangeRequired(
Ed Tanousace85d62021-10-26 12:45:59 -0700216 asyncResp->res, crow::utility::urlFromPieces(
217 "redfish", "v1", "AccountService",
218 "Accounts", req.session->username));
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700219 }
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100220
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700221 fillSessionObject(asyncResp->res, *session);
222 });
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100223
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700224 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
Ed Tanoused398212021-06-09 17:05:54 -0700225 .privileges(redfish::privileges::getSessionService)
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700226 .methods(boost::beast::http::verb::get)(
227 [](const crow::Request& /* req */,
228 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
229 asyncResp->res.jsonValue["@odata.type"] =
230 "#SessionService.v1_0_2.SessionService";
231 asyncResp->res.jsonValue["@odata.id"] =
232 "/redfish/v1/SessionService/";
233 asyncResp->res.jsonValue["Name"] = "Session Service";
234 asyncResp->res.jsonValue["Id"] = "SessionService";
235 asyncResp->res.jsonValue["Description"] = "Session Service";
236 asyncResp->res.jsonValue["SessionTimeout"] =
237 persistent_data::SessionStore::getInstance()
238 .getTimeoutInSeconds();
239 asyncResp->res.jsonValue["ServiceEnabled"] = true;
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800240
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700241 asyncResp->res.jsonValue["Sessions"] = {
242 {"@odata.id", "/redfish/v1/SessionService/Sessions"}};
243 });
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100244
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700245 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
Ed Tanoused398212021-06-09 17:05:54 -0700246 .privileges(redfish::privileges::patchSessionService)
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700247 .methods(boost::beast::http::verb::patch)(
248 [](const crow::Request& req,
249 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
250 std::optional<int64_t> sessionTimeout;
Willy Tu15ed6782021-12-14 11:03:16 -0800251 if (!json_util::readJsonPatch(req, asyncResp->res,
252 "SessionTimeout", sessionTimeout))
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700253 {
254 return;
255 }
Ed Tanous0f74e642018-11-12 15:17:05 -0800256
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700257 if (sessionTimeout)
258 {
259 // The mininum & maximum allowed values for session timeout
260 // are 30 seconds and 86400 seconds respectively as per the
261 // session service schema mentioned at
262 // https://redfish.dmtf.org/schemas/v1/SessionService.v1_1_7.json
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530263
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700264 if (*sessionTimeout <= 86400 && *sessionTimeout >= 30)
265 {
266 std::chrono::seconds sessionTimeoutInseconds(
267 *sessionTimeout);
268 persistent_data::SessionStore::getInstance()
269 .updateSessionTimeout(sessionTimeoutInseconds);
270 messages::propertyValueModified(
271 asyncResp->res, "SessionTimeOut",
272 std::to_string(*sessionTimeout));
273 }
274 else
275 {
276 messages::propertyValueNotInList(
277 asyncResp->res, std::to_string(*sessionTimeout),
278 "SessionTimeOut");
279 }
280 }
281 });
282}
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100283
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284} // namespace redfish