xref: /openbmc/bmcweb/features/redfish/lib/redfish_sessions.hpp (revision 8d1b46d7f8d39db2ba048f9e9007106ca3a28c9b)
12b7981f6SKowalski, Kamil /*
22b7981f6SKowalski, Kamil // Copyright (c) 2018 Intel Corporation
32b7981f6SKowalski, Kamil //
42b7981f6SKowalski, Kamil // Licensed under the Apache License, Version 2.0 (the "License");
52b7981f6SKowalski, Kamil // you may not use this file except in compliance with the License.
62b7981f6SKowalski, Kamil // You may obtain a copy of the License at
72b7981f6SKowalski, Kamil //
82b7981f6SKowalski, Kamil //      http://www.apache.org/licenses/LICENSE-2.0
92b7981f6SKowalski, Kamil //
102b7981f6SKowalski, Kamil // Unless required by applicable law or agreed to in writing, software
112b7981f6SKowalski, Kamil // distributed under the License is distributed on an "AS IS" BASIS,
122b7981f6SKowalski, Kamil // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132b7981f6SKowalski, Kamil // See the License for the specific language governing permissions and
142b7981f6SKowalski, Kamil // limitations under the License.
152b7981f6SKowalski, Kamil */
162b7981f6SKowalski, Kamil #pragma once
1743a095abSBorawski.Lukasz 
18f4c4dcf4SKowalski, Kamil #include "error_messages.hpp"
192b7981f6SKowalski, Kamil #include "node.hpp"
2052cc112dSEd Tanous #include "persistent_data.hpp"
212b7981f6SKowalski, Kamil 
221abe55efSEd Tanous namespace redfish
231abe55efSEd Tanous {
242b7981f6SKowalski, Kamil 
252b7981f6SKowalski, Kamil class SessionCollection;
262b7981f6SKowalski, Kamil 
271abe55efSEd Tanous class Sessions : public Node
281abe55efSEd Tanous {
292b7981f6SKowalski, Kamil   public:
3052cc112dSEd Tanous     Sessions(App& app) :
311abe55efSEd Tanous         Node(app, "/redfish/v1/SessionService/Sessions/<str>/", std::string())
321abe55efSEd Tanous     {
33e0d918bcSEd Tanous         entityPrivileges = {
34e0d918bcSEd Tanous             {boost::beast::http::verb::get, {{"Login"}}},
35e0d918bcSEd Tanous             {boost::beast::http::verb::head, {{"Login"}}},
36e0d918bcSEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
37e0d918bcSEd Tanous             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
38900f9497SJoseph Reynolds             {boost::beast::http::verb::delete_,
39900f9497SJoseph Reynolds              {{"ConfigureManager"}, {"ConfigureSelf"}}},
40e0d918bcSEd Tanous             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
412b7981f6SKowalski, Kamil     }
422b7981f6SKowalski, Kamil 
432b7981f6SKowalski, Kamil   private:
44*8d1b46d7Szhanghch05     void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
45*8d1b46d7Szhanghch05                const crow::Request&,
461abe55efSEd Tanous                const std::vector<std::string>& params) override
471abe55efSEd Tanous     {
48900f9497SJoseph Reynolds         // Note that control also reaches here via doPost and doDelete.
492b7981f6SKowalski, Kamil         auto session =
5052cc112dSEd Tanous             persistent_data::SessionStore::getInstance().getSessionByUid(
514b1b8683SBorawski.Lukasz                 params[0]);
522b7981f6SKowalski, Kamil 
531abe55efSEd Tanous         if (session == nullptr)
541abe55efSEd Tanous         {
55*8d1b46d7Szhanghch05             messages::resourceNotFound(asyncResp->res, "Session", params[0]);
562b7981f6SKowalski, Kamil             return;
572b7981f6SKowalski, Kamil         }
582b7981f6SKowalski, Kamil 
59*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Id"] = session->uniqueId;
60*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["UserName"] = session->username;
61*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.id"] =
6255c7b7a2SEd Tanous             "/redfish/v1/SessionService/Sessions/" + session->uniqueId;
63*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.type"] = "#Session.v1_3_0.Session";
64*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Name"] = "User Session";
65*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Description"] = "Manager User Session";
66*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["ClientOriginIPAddress"] = session->clientIp;
67c0ea7ae1SSunitha Harish #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
68*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Oem"]["OpenBMC"]["@odata.type"] =
6908bdcc71SSunitha Harish             "#OemSession.v1_0_0.Session";
70*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Oem"]["OpenBMC"]["ClientID"] =
71*8d1b46d7Szhanghch05             session->clientId;
7208bdcc71SSunitha Harish #endif
732b7981f6SKowalski, Kamil     }
742b7981f6SKowalski, Kamil 
75*8d1b46d7Szhanghch05     void doDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
76*8d1b46d7Szhanghch05                   const crow::Request& req,
771abe55efSEd Tanous                   const std::vector<std::string>& params) override
781abe55efSEd Tanous     {
792b7981f6SKowalski, Kamil         // Need only 1 param which should be id of session to be deleted
801abe55efSEd Tanous         if (params.size() != 1)
811abe55efSEd Tanous         {
82f4c4dcf4SKowalski, Kamil             // This should be handled by crow and never happen
831abe55efSEd Tanous             BMCWEB_LOG_ERROR << "Session DELETE has been called with invalid "
841abe55efSEd Tanous                                 "number of params";
85f4c4dcf4SKowalski, Kamil 
86*8d1b46d7Szhanghch05             messages::generalError(asyncResp->res);
872b7981f6SKowalski, Kamil             return;
882b7981f6SKowalski, Kamil         }
892b7981f6SKowalski, Kamil 
902b7981f6SKowalski, Kamil         auto session =
9152cc112dSEd Tanous             persistent_data::SessionStore::getInstance().getSessionByUid(
924b1b8683SBorawski.Lukasz                 params[0]);
932b7981f6SKowalski, Kamil 
941abe55efSEd Tanous         if (session == nullptr)
951abe55efSEd Tanous         {
96*8d1b46d7Szhanghch05             messages::resourceNotFound(asyncResp->res, "Session", params[0]);
972b7981f6SKowalski, Kamil             return;
982b7981f6SKowalski, Kamil         }
992b7981f6SKowalski, Kamil 
100900f9497SJoseph Reynolds         // Perform a proper ConfigureSelf authority check.  If a
101900f9497SJoseph Reynolds         // session is being used to DELETE some other user's session,
102900f9497SJoseph Reynolds         // then the ConfigureSelf privilege does not apply.  In that
103900f9497SJoseph Reynolds         // case, perform the authority check again without the user's
104900f9497SJoseph Reynolds         // ConfigureSelf privilege.
105900f9497SJoseph Reynolds         if (session->username != req.session->username)
106900f9497SJoseph Reynolds         {
107900f9497SJoseph Reynolds             if (!isAllowedWithoutConfigureSelf(req))
108900f9497SJoseph Reynolds             {
109900f9497SJoseph Reynolds                 BMCWEB_LOG_WARNING << "DELETE Session denied access";
110*8d1b46d7Szhanghch05                 messages::insufficientPrivilege(asyncResp->res);
111900f9497SJoseph Reynolds                 return;
112900f9497SJoseph Reynolds             }
113900f9497SJoseph Reynolds         }
114900f9497SJoseph Reynolds 
115f4c4dcf4SKowalski, Kamil         // DELETE should return representation of object that will be removed
116*8d1b46d7Szhanghch05         doGet(asyncResp, req, params);
117f4c4dcf4SKowalski, Kamil 
11852cc112dSEd Tanous         persistent_data::SessionStore::getInstance().removeSession(session);
1192b7981f6SKowalski, Kamil     }
1202b7981f6SKowalski, Kamil 
1212b7981f6SKowalski, Kamil     /**
1222b7981f6SKowalski, Kamil      * This allows SessionCollection to reuse this class' doGet method, to
1231abe55efSEd Tanous      * maintain consistency of returned data, as Collection's doPost should
12492f68223SSunitha Harish      * return data for created member which should match member's doGet
12592f68223SSunitha Harish      * result in 100%
1262b7981f6SKowalski, Kamil      */
1272b7981f6SKowalski, Kamil     friend SessionCollection;
1282b7981f6SKowalski, Kamil };
1292b7981f6SKowalski, Kamil 
1301abe55efSEd Tanous class SessionCollection : public Node
1311abe55efSEd Tanous {
1322b7981f6SKowalski, Kamil   public:
13352cc112dSEd Tanous     SessionCollection(App& app) :
1341abe55efSEd Tanous         Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app)
1351abe55efSEd Tanous     {
136e0d918bcSEd Tanous         entityPrivileges = {
137e0d918bcSEd Tanous             {boost::beast::http::verb::get, {{"Login"}}},
138e0d918bcSEd Tanous             {boost::beast::http::verb::head, {{"Login"}}},
139e0d918bcSEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
140e0d918bcSEd Tanous             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
141e0d918bcSEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
142e0d918bcSEd Tanous             {boost::beast::http::verb::post, {}}};
1432b7981f6SKowalski, Kamil     }
1442b7981f6SKowalski, Kamil 
1452b7981f6SKowalski, Kamil   private:
146*8d1b46d7Szhanghch05     void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
147*8d1b46d7Szhanghch05                const crow::Request&, const std::vector<std::string>&) override
1481abe55efSEd Tanous     {
14955c7b7a2SEd Tanous         std::vector<const std::string*> sessionIds =
15052cc112dSEd Tanous             persistent_data::SessionStore::getInstance().getUniqueIds(
15152cc112dSEd Tanous                 false, persistent_data::PersistenceType::TIMEOUT);
1522b7981f6SKowalski, Kamil 
153*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Members@odata.count"] = sessionIds.size();
154*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
1551abe55efSEd Tanous         for (const std::string* uid : sessionIds)
1561abe55efSEd Tanous         {
157*8d1b46d7Szhanghch05             asyncResp->res.jsonValue["Members"].push_back(
1582b7981f6SKowalski, Kamil                 {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}});
1592b7981f6SKowalski, Kamil         }
160*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Members@odata.count"] = sessionIds.size();
161*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.type"] =
162*8d1b46d7Szhanghch05             "#SessionCollection.SessionCollection";
163*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.id"] =
164*8d1b46d7Szhanghch05             "/redfish/v1/SessionService/Sessions/";
165*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Name"] = "Session Collection";
166*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Description"] = "Session Collection";
1672b7981f6SKowalski, Kamil     }
1682b7981f6SKowalski, Kamil 
169*8d1b46d7Szhanghch05     void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
170*8d1b46d7Szhanghch05                 const crow::Request& req,
171cb13a392SEd Tanous                 const std::vector<std::string>&) override
1721abe55efSEd Tanous     {
1739712f8acSEd Tanous         std::string username;
1749712f8acSEd Tanous         std::string password;
17508bdcc71SSunitha Harish         std::optional<nlohmann::json> oemObject;
17608bdcc71SSunitha Harish         std::string clientId;
177*8d1b46d7Szhanghch05         if (!json_util::readJson(req, asyncResp->res, "UserName", username,
178*8d1b46d7Szhanghch05                                  "Password", password, "Oem", oemObject))
1791abe55efSEd Tanous         {
1802b7981f6SKowalski, Kamil             return;
1812b7981f6SKowalski, Kamil         }
1822b7981f6SKowalski, Kamil 
183820ce598SEd Tanous         if (password.empty() || username.empty() ||
184*8d1b46d7Szhanghch05             asyncResp->res.result() != boost::beast::http::status::ok)
1851abe55efSEd Tanous         {
1861abe55efSEd Tanous             if (username.empty())
1871abe55efSEd Tanous             {
188*8d1b46d7Szhanghch05                 messages::propertyMissing(asyncResp->res, "UserName");
189f4c4dcf4SKowalski, Kamil             }
190f4c4dcf4SKowalski, Kamil 
1911abe55efSEd Tanous             if (password.empty())
1921abe55efSEd Tanous             {
193*8d1b46d7Szhanghch05                 messages::propertyMissing(asyncResp->res, "Password");
194820ce598SEd Tanous             }
195820ce598SEd Tanous 
196820ce598SEd Tanous             return;
197f4c4dcf4SKowalski, Kamil         }
1982b7981f6SKowalski, Kamil 
1993bf4e632SJoseph Reynolds         int pamrc = pamAuthenticateUser(username, password);
2003bf4e632SJoseph Reynolds         bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
2013bf4e632SJoseph Reynolds         if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
2021abe55efSEd Tanous         {
203*8d1b46d7Szhanghch05             messages::resourceAtUriUnauthorized(asyncResp->res,
204*8d1b46d7Szhanghch05                                                 std::string(req.url),
205f12894f8SJason M. Bills                                                 "Invalid username or password");
206820ce598SEd Tanous             return;
2072b7981f6SKowalski, Kamil         }
20808bdcc71SSunitha Harish #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
20908bdcc71SSunitha Harish         if (oemObject)
21008bdcc71SSunitha Harish         {
21108bdcc71SSunitha Harish             std::optional<nlohmann::json> bmcOem;
212*8d1b46d7Szhanghch05             if (!json_util::readJson(*oemObject, asyncResp->res, "OpenBMC",
213*8d1b46d7Szhanghch05                                      bmcOem))
21408bdcc71SSunitha Harish             {
21508bdcc71SSunitha Harish                 return;
21608bdcc71SSunitha Harish             }
217*8d1b46d7Szhanghch05             if (!json_util::readJson(*bmcOem, asyncResp->res, "ClientID",
218*8d1b46d7Szhanghch05                                      clientId))
21908bdcc71SSunitha Harish             {
22008bdcc71SSunitha Harish                 BMCWEB_LOG_ERROR << "Could not read ClientId";
22108bdcc71SSunitha Harish                 return;
22208bdcc71SSunitha Harish             }
22308bdcc71SSunitha Harish         }
22408bdcc71SSunitha Harish #endif
2256f115bbbSManojkiran Eda 
226820ce598SEd Tanous         // User is authenticated - create session
22752cc112dSEd Tanous         std::shared_ptr<persistent_data::UserSession> session =
22852cc112dSEd Tanous             persistent_data::SessionStore::getInstance().generateUserSession(
229d3239224SSunitha Harish                 username, req.ipAddress.to_string(), clientId,
230d3239224SSunitha Harish                 persistent_data::PersistenceType::TIMEOUT, isConfigureSelfOnly);
231*8d1b46d7Szhanghch05         asyncResp->res.addHeader("X-Auth-Token", session->sessionToken);
232*8d1b46d7Szhanghch05         asyncResp->res.addHeader("Location",
233*8d1b46d7Szhanghch05                                  "/redfish/v1/SessionService/Sessions/" +
234820ce598SEd Tanous                                      session->uniqueId);
235*8d1b46d7Szhanghch05         asyncResp->res.result(boost::beast::http::status::created);
2363bf4e632SJoseph Reynolds         if (session->isConfigureSelfOnly)
2373bf4e632SJoseph Reynolds         {
2383bf4e632SJoseph Reynolds             messages::passwordChangeRequired(
239*8d1b46d7Szhanghch05                 asyncResp->res,
2403bf4e632SJoseph Reynolds                 "/redfish/v1/AccountService/Accounts/" + session->username);
2413bf4e632SJoseph Reynolds         }
242*8d1b46d7Szhanghch05         memberSession.doGet(asyncResp, req, {session->uniqueId});
2432b7981f6SKowalski, Kamil     }
2442b7981f6SKowalski, Kamil 
2452b7981f6SKowalski, Kamil     /**
2462b7981f6SKowalski, Kamil      * Member session to ensure consistency between collection's doPost and
2472b7981f6SKowalski, Kamil      * member's doGet, as they should return 100% matching data
2482b7981f6SKowalski, Kamil      */
2492b7981f6SKowalski, Kamil     Sessions memberSession;
2502b7981f6SKowalski, Kamil };
2512b7981f6SKowalski, Kamil 
2521abe55efSEd Tanous class SessionService : public Node
2531abe55efSEd Tanous {
2545d27b854SBorawski.Lukasz   public:
25552cc112dSEd Tanous     SessionService(App& app) : Node(app, "/redfish/v1/SessionService/")
2561abe55efSEd Tanous     {
2573ebd75f7SEd Tanous 
258e0d918bcSEd Tanous         entityPrivileges = {
259e0d918bcSEd Tanous             {boost::beast::http::verb::get, {{"Login"}}},
260e0d918bcSEd Tanous             {boost::beast::http::verb::head, {{"Login"}}},
261e0d918bcSEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
262e0d918bcSEd Tanous             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
263e0d918bcSEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
264e0d918bcSEd Tanous             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2655d27b854SBorawski.Lukasz     }
2665d27b854SBorawski.Lukasz 
2675d27b854SBorawski.Lukasz   private:
268*8d1b46d7Szhanghch05     void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
269*8d1b46d7Szhanghch05                const crow::Request&, const std::vector<std::string>&) override
2701abe55efSEd Tanous     {
271*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.type"] =
272*8d1b46d7Szhanghch05             "#SessionService.v1_0_2.SessionService";
273*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/";
274*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Name"] = "Session Service";
275*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Id"] = "SessionService";
276*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Description"] = "Session Service";
277*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["SessionTimeout"] =
27852cc112dSEd Tanous             persistent_data::SessionStore::getInstance().getTimeoutInSeconds();
279*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["ServiceEnabled"] = true;
2800f74e643SEd Tanous 
281*8d1b46d7Szhanghch05         asyncResp->res.jsonValue["Sessions"] = {
2820f261533SEd Tanous             {"@odata.id", "/redfish/v1/SessionService/Sessions"}};
2835d27b854SBorawski.Lukasz     }
284f2a4a606SManojkiran Eda 
285*8d1b46d7Szhanghch05     void doPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
286*8d1b46d7Szhanghch05                  const crow::Request& req,
287f2a4a606SManojkiran Eda                  const std::vector<std::string>&) override
288f2a4a606SManojkiran Eda     {
289f2a4a606SManojkiran Eda         std::optional<int64_t> sessionTimeout;
290*8d1b46d7Szhanghch05         if (!json_util::readJson(req, asyncResp->res, "SessionTimeout",
291*8d1b46d7Szhanghch05                                  sessionTimeout))
292f2a4a606SManojkiran Eda         {
293f2a4a606SManojkiran Eda             return;
294f2a4a606SManojkiran Eda         }
295f2a4a606SManojkiran Eda 
296f2a4a606SManojkiran Eda         if (sessionTimeout)
297f2a4a606SManojkiran Eda         {
298f2a4a606SManojkiran Eda             // The mininum & maximum allowed values for session timeout are 30
299f2a4a606SManojkiran Eda             // seconds and 86400 seconds respectively as per the session service
300f2a4a606SManojkiran Eda             // schema mentioned at
301f2a4a606SManojkiran Eda             // https://redfish.dmtf.org/schemas/v1/SessionService.v1_1_7.json
302f2a4a606SManojkiran Eda 
303f2a4a606SManojkiran Eda             if (*sessionTimeout <= 86400 && *sessionTimeout >= 30)
304f2a4a606SManojkiran Eda             {
305f2a4a606SManojkiran Eda                 std::chrono::seconds sessionTimeoutInseconds(*sessionTimeout);
306f2a4a606SManojkiran Eda                 persistent_data::SessionStore::getInstance()
307f2a4a606SManojkiran Eda                     .updateSessionTimeout(sessionTimeoutInseconds);
308f2a4a606SManojkiran Eda                 messages::propertyValueModified(
309f2a4a606SManojkiran Eda                     asyncResp->res, "SessionTimeOut",
310f2a4a606SManojkiran Eda                     std::to_string(*sessionTimeout));
311f2a4a606SManojkiran Eda             }
312f2a4a606SManojkiran Eda             else
313f2a4a606SManojkiran Eda             {
314f2a4a606SManojkiran Eda                 messages::propertyValueNotInList(
315*8d1b46d7Szhanghch05                     asyncResp->res, std::to_string(*sessionTimeout),
316*8d1b46d7Szhanghch05                     "SessionTimeOut");
317f2a4a606SManojkiran Eda             }
318f2a4a606SManojkiran Eda         }
319f2a4a606SManojkiran Eda     }
3205d27b854SBorawski.Lukasz };
3215d27b854SBorawski.Lukasz 
3222b7981f6SKowalski, Kamil } // namespace redfish
323