blob: 53ec7d07c3de4dac0b1ab4ca33d28dba71aef039 [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>
22
Ed Tanous1abe55e2018-09-05 08:30:59 -070023namespace redfish
24{
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010025
26class SessionCollection;
27
Ed Tanousfaa34cc2021-06-03 13:27:02 -070028void fillSessionObject(crow::Response& res,
29 const persistent_data::UserSession& session)
Ed Tanous1abe55e2018-09-05 08:30:59 -070030{
Ed Tanousfaa34cc2021-06-03 13:27:02 -070031 res.jsonValue["Id"] = session.uniqueId;
32 res.jsonValue["UserName"] = session.username;
33 res.jsonValue["@odata.id"] =
34 "/redfish/v1/SessionService/Sessions/" + session.uniqueId;
35 res.jsonValue["@odata.type"] = "#Session.v1_3_0.Session";
36 res.jsonValue["Name"] = "User Session";
37 res.jsonValue["Description"] = "Manager User Session";
38 res.jsonValue["ClientOriginIPAddress"] = session.clientIp;
Sunitha Harishc0ea7ae2020-10-30 02:37:30 -050039#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
Ed Tanousfaa34cc2021-06-03 13:27:02 -070040 res.jsonValue["Oem"]["OpenBMC"]["@odata.type"] =
41 "#OemSession.v1_0_0.Session";
42 res.jsonValue["Oem"]["OpenBMC"]["ClientID"] = session.clientId;
Sunitha Harish08bdcc72020-05-12 05:17:57 -050043#endif
Ed Tanousfaa34cc2021-06-03 13:27:02 -070044}
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010045
Ed Tanousfaa34cc2021-06-03 13:27:02 -070046inline void requestRoutesSession(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -070047{
Ed Tanousfaa34cc2021-06-03 13:27:02 -070048 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
49 .privileges({{"Login"}})
50 .methods(boost::beast::http::verb::get)(
51 [](const crow::Request& /*req*/,
52 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
53 const std::string& sessionId) -> void {
54 // Note that control also reaches here via doPost and doDelete.
55 auto session = persistent_data::SessionStore::getInstance()
56 .getSessionByUid(sessionId);
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010057
Ed Tanousfaa34cc2021-06-03 13:27:02 -070058 if (session == nullptr)
59 {
60 messages::resourceNotFound(asyncResp->res, "Session",
61 sessionId);
62 return;
63 }
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010064
Ed Tanousfaa34cc2021-06-03 13:27:02 -070065 fillSessionObject(asyncResp->res, *session);
66 });
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010067
Ed Tanousfaa34cc2021-06-03 13:27:02 -070068 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/")
69 .privileges({{"ConfigureManager"}, {"ConfigureSelf"}})
70 .methods(boost::beast::http::verb::delete_)(
71 [](const crow::Request& req,
72 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
73 const std::string& sessionId) -> void {
74 auto session = persistent_data::SessionStore::getInstance()
75 .getSessionByUid(sessionId);
Ed Tanousb9845d92018-07-24 14:38:06 -070076
Ed Tanousfaa34cc2021-06-03 13:27:02 -070077 if (session == nullptr)
78 {
79 messages::resourceNotFound(asyncResp->res, "Session",
80 sessionId);
81 return;
82 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070083
Ed Tanousfaa34cc2021-06-03 13:27:02 -070084 // Perform a proper ConfigureSelf authority check. If a
85 // session is being used to DELETE some other user's session,
86 // then the ConfigureSelf privilege does not apply. In that
87 // case, perform the authority check again without the user's
88 // ConfigureSelf privilege.
89 if (session->username != req.session->username)
90 {
91 Privileges effectiveUserPrivileges =
92 redfish::getUserPrivileges(req.userRole);
Ed Tanous1abe55e2018-09-05 08:30:59 -070093
Ed Tanousfaa34cc2021-06-03 13:27:02 -070094 if (!effectiveUserPrivileges.isSupersetOf(
95 {{"ConfigureUsers"}}))
96 {
97 messages::insufficientPrivilege(asyncResp->res);
98 return;
99 }
100 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700102 persistent_data::SessionStore::getInstance().removeSession(
103 session);
104 asyncResp->res.result(boost::beast::http::status::no_content);
105 });
106
107 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
108 .privileges({{"Login"}})
109 .methods(boost::beast::http::verb::get)(
110 [](const crow::Request& /*req*/,
111 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
112 std::vector<const std::string*> sessionIds =
113 persistent_data::SessionStore::getInstance().getUniqueIds(
114 false, persistent_data::PersistenceType::TIMEOUT);
115
116 asyncResp->res.jsonValue["Members@odata.count"] =
117 sessionIds.size();
118 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
119 for (const std::string* uid : sessionIds)
120 {
121 asyncResp->res.jsonValue["Members"].push_back(
122 {{"@odata.id",
123 "/redfish/v1/SessionService/Sessions/" + *uid}});
124 }
125 asyncResp->res.jsonValue["Members@odata.count"] =
126 sessionIds.size();
127 asyncResp->res.jsonValue["@odata.type"] =
128 "#SessionCollection.SessionCollection";
129 asyncResp->res.jsonValue["@odata.id"] =
130 "/redfish/v1/SessionService/Sessions/";
131 asyncResp->res.jsonValue["Name"] = "Session Collection";
132 asyncResp->res.jsonValue["Description"] = "Session Collection";
133 });
134
135 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/")
136 .privileges({})
137 .methods(boost::beast::http::verb::post)(
138 [](const crow::Request& req,
139 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
140 std::string username;
141 std::string password;
142 std::optional<nlohmann::json> oemObject;
143 std::string clientId;
144 if (!json_util::readJson(req, asyncResp->res, "UserName",
145 username, "Password", password, "Oem",
146 oemObject))
147 {
148 return;
149 }
150
151 if (password.empty() || username.empty() ||
152 asyncResp->res.result() != boost::beast::http::status::ok)
153 {
154 if (username.empty())
155 {
156 messages::propertyMissing(asyncResp->res, "UserName");
157 }
158
159 if (password.empty())
160 {
161 messages::propertyMissing(asyncResp->res, "Password");
162 }
163
164 return;
165 }
166
167 int pamrc = pamAuthenticateUser(username, password);
168 bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
169 if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
170 {
171 messages::resourceAtUriUnauthorized(
172 asyncResp->res, std::string(req.url),
173 "Invalid username or password");
174 return;
175 }
Sunitha Harish08bdcc72020-05-12 05:17:57 -0500176#ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700177 if (oemObject)
178 {
179 std::optional<nlohmann::json> bmcOem;
180 if (!json_util::readJson(*oemObject, asyncResp->res,
181 "OpenBMC", bmcOem))
182 {
183 return;
184 }
185 if (!json_util::readJson(*bmcOem, asyncResp->res,
186 "ClientID", clientId))
187 {
188 BMCWEB_LOG_ERROR << "Could not read ClientId";
189 return;
190 }
191 }
Sunitha Harish08bdcc72020-05-12 05:17:57 -0500192#endif
Manojkiran Eda6f115bb2020-06-27 09:52:23 +0530193
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700194 // User is authenticated - create session
195 std::shared_ptr<persistent_data::UserSession> session =
196 persistent_data::SessionStore::getInstance()
197 .generateUserSession(
198 username, req.ipAddress.to_string(), clientId,
199 persistent_data::PersistenceType::TIMEOUT,
200 isConfigureSelfOnly);
201 asyncResp->res.addHeader("X-Auth-Token", session->sessionToken);
202 asyncResp->res.addHeader(
203 "Location",
204 "/redfish/v1/SessionService/Sessions/" + session->uniqueId);
205 asyncResp->res.result(boost::beast::http::status::created);
206 if (session->isConfigureSelfOnly)
207 {
208 messages::passwordChangeRequired(
209 asyncResp->res, "/redfish/v1/AccountService/Accounts/" +
210 session->username);
211 }
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100212
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700213 fillSessionObject(asyncResp->res, *session);
214 });
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100215
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700216 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
217 .privileges({{"Login"}})
218 .methods(boost::beast::http::verb::get)(
219 [](const crow::Request& /* req */,
220 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
221 asyncResp->res.jsonValue["@odata.type"] =
222 "#SessionService.v1_0_2.SessionService";
223 asyncResp->res.jsonValue["@odata.id"] =
224 "/redfish/v1/SessionService/";
225 asyncResp->res.jsonValue["Name"] = "Session Service";
226 asyncResp->res.jsonValue["Id"] = "SessionService";
227 asyncResp->res.jsonValue["Description"] = "Session Service";
228 asyncResp->res.jsonValue["SessionTimeout"] =
229 persistent_data::SessionStore::getInstance()
230 .getTimeoutInSeconds();
231 asyncResp->res.jsonValue["ServiceEnabled"] = true;
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800232
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700233 asyncResp->res.jsonValue["Sessions"] = {
234 {"@odata.id", "/redfish/v1/SessionService/Sessions"}};
235 });
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100236
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700237 BMCWEB_ROUTE(app, "/redfish/v1/SessionService/")
238 .privileges({{"ConfigureManager"}})
239 .methods(boost::beast::http::verb::patch)(
240 [](const crow::Request& req,
241 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void {
242 std::optional<int64_t> sessionTimeout;
243 if (!json_util::readJson(req, asyncResp->res, "SessionTimeout",
244 sessionTimeout))
245 {
246 return;
247 }
Ed Tanous0f74e642018-11-12 15:17:05 -0800248
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700249 if (sessionTimeout)
250 {
251 // The mininum & maximum allowed values for session timeout
252 // are 30 seconds and 86400 seconds respectively as per the
253 // session service schema mentioned at
254 // https://redfish.dmtf.org/schemas/v1/SessionService.v1_1_7.json
Manojkiran Edaf2a4a602020-08-27 16:04:26 +0530255
Ed Tanousfaa34cc2021-06-03 13:27:02 -0700256 if (*sessionTimeout <= 86400 && *sessionTimeout >= 30)
257 {
258 std::chrono::seconds sessionTimeoutInseconds(
259 *sessionTimeout);
260 persistent_data::SessionStore::getInstance()
261 .updateSessionTimeout(sessionTimeoutInseconds);
262 messages::propertyValueModified(
263 asyncResp->res, "SessionTimeOut",
264 std::to_string(*sessionTimeout));
265 }
266 else
267 {
268 messages::propertyValueNotInList(
269 asyncResp->res, std::to_string(*sessionTimeout),
270 "SessionTimeOut");
271 }
272 }
273 });
274}
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100275
Ed Tanous1abe55e2018-09-05 08:30:59 -0700276} // namespace redfish