blob: 75902681a0f730d382aa63ab5bea0e0beaaba94d [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"
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010019#include "node.hpp"
Ed Tanous52cc1122020-07-18 13:51:21 -070020#include "persistent_data.hpp"
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010021
John Edward Broadbent7e860f12021-04-08 15:57:16 -070022#include <app.hpp>
23
Ed Tanous1abe55e2018-09-05 08:30:59 -070024namespace redfish
25{
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010026
27class SessionCollection;
28
Ed Tanousfaa34cc2021-06-03 13:27:02 -070029void 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>/")
50 .privileges({{"Login"}})
51 .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>/")
70 .privileges({{"ConfigureManager"}, {"ConfigureSelf"}})
71 .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(
96 {{"ConfigureUsers"}}))
97 {
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);
105 asyncResp->res.result(boost::beast::http::status::no_content);
106 });
107
108 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
109 .privileges({{"Login"}})
110 .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/")
137 .privileges({})
138 .methods(boost::beast::http::verb::post)(
139 [](const crow::Request& req,
140 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
141 std::string username;
142 std::string password;
143 std::optional<nlohmann::json> oemObject;
144 std::string clientId;
145 if (!json_util::readJson(req, asyncResp->res, "UserName",
146 username, "Password", password, "Oem",
147 oemObject))
148 {
149 return;
150 }
151
152 if (password.empty() || username.empty() ||
153 asyncResp->res.result() != boost::beast::http::status::ok)
154 {
155 if (username.empty())
156 {
157 messages::propertyMissing(asyncResp->res, "UserName");
158 }
159
160 if (password.empty())
161 {
162 messages::propertyMissing(asyncResp->res, "Password");
163 }
164
165 return;
166 }
167
168 int pamrc = pamAuthenticateUser(username, password);
169 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
170 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
171 {
172 messages::resourceAtUriUnauthorized(
173 asyncResp->res, std::string(req.url),
174 "Invalid username or password");
175 return;
176 }
Sunitha Harish08bdcc72020-05-12 05:17:57 -0500177#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700178 if (oemObject)
179 {
180 std::optional<nlohmann::json> bmcOem;
181 if (!json_util::readJson(*oemObject, asyncResp->res,
182 "OpenBMC", bmcOem))
183 {
184 return;
185 }
186 if (!json_util::readJson(*bmcOem, asyncResp->res,
187 "ClientID", clientId))
188 {
189 BMCWEB_LOG_ERROR << "Could not read ClientId";
190 return;
191 }
192 }
Sunitha Harish08bdcc72020-05-12 05:17:57 -0500193#endif
Manojkiran Eda6f115bb2020-06-27 09:52:23 +0530194
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700195 // User is authenticated - create session
196 std::shared_ptr<persistent_data::UserSession> session =
197 persistent_data::SessionStore::getInstance()
198 .generateUserSession(
199 username, req.ipAddress.to_string(), clientId,
200 persistent_data::PersistenceType::TIMEOUT,
201 isConfigureSelfOnly);
202 asyncResp->res.addHeader("X-Auth-Token", session->sessionToken);
203 asyncResp->res.addHeader(
204 "Location",
205 "/redfish/v1/SessionService/Sessions/" + session->uniqueId);
206 asyncResp->res.result(boost::beast::http::status::created);
207 if (session->isConfigureSelfOnly)
208 {
209 messages::passwordChangeRequired(
210 asyncResp->res, "/redfish/v1/AccountService/Accounts/" +
211 session->username);
212 }
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100213
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700214 fillSessionObject(asyncResp->res, *session);
215 });
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100216
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700217 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
218 .privileges({{"Login"}})
219 .methods(boost::beast::http::verb::get)(
220 [](const crow::Request& /* req */,
221 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
222 asyncResp->res.jsonValue["@odata.type"] =
223 "#SessionService.v1_0_2.SessionService";
224 asyncResp->res.jsonValue["@odata.id"] =
225 "/redfish/v1/SessionService/";
226 asyncResp->res.jsonValue["Name"] = "Session Service";
227 asyncResp->res.jsonValue["Id"] = "SessionService";
228 asyncResp->res.jsonValue["Description"] = "Session Service";
229 asyncResp->res.jsonValue["SessionTimeout"] =
230 persistent_data::SessionStore::getInstance()
231 .getTimeoutInSeconds();
232 asyncResp->res.jsonValue["ServiceEnabled"] = true;
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800233
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700234 asyncResp->res.jsonValue["Sessions"] = {
235 {"@odata.id", "/redfish/v1/SessionService/Sessions"}};
236 });
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100237
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700238 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
239 .privileges({{"ConfigureManager"}})
240 .methods(boost::beast::http::verb::patch)(
241 [](const crow::Request& req,
242 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
243 std::optional<int64_t> sessionTimeout;
244 if (!json_util::readJson(req, asyncResp->res, "SessionTimeout",
245 sessionTimeout))
246 {
247 return;
248 }
Ed Tanous0f74e642018-11-12 15:17:05 -0800249
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700250 if (sessionTimeout)
251 {
252 // The mininum & maximum allowed values for session timeout
253 // are 30 seconds and 86400 seconds respectively as per the
254 // session service schema mentioned at
255 // https://redfish.dmtf.org/schemas/v1/SessionService.v1_1_7.json
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530256
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700257 if (*sessionTimeout <= 86400 && *sessionTimeout >= 30)
258 {
259 std::chrono::seconds sessionTimeoutInseconds(
260 *sessionTimeout);
261 persistent_data::SessionStore::getInstance()
262 .updateSessionTimeout(sessionTimeoutInseconds);
263 messages::propertyValueModified(
264 asyncResp->res, "SessionTimeOut",
265 std::to_string(*sessionTimeout));
266 }
267 else
268 {
269 messages::propertyValueNotInList(
270 asyncResp->res, std::to_string(*sessionTimeout),
271 "SessionTimeOut");
272 }
273 }
274 });
275}
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100276
Ed Tanous1abe55e2018-09-05 08:30:59 -0700277} // namespace redfish