xref: /openbmc/bmcweb/features/redfish/lib/redfish_sessions.hpp (revision 3bf4e63296f0b69201904b03b2470543a7e0c627)
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"
204b1b8683SBorawski.Lukasz #include "persistent_data_middleware.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:
301abe55efSEd Tanous     Sessions(CrowApp& 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:
4455c7b7a2SEd Tanous     void doGet(crow::Response& res, const crow::Request& req,
451abe55efSEd Tanous                const std::vector<std::string>& params) override
461abe55efSEd Tanous     {
47900f9497SJoseph Reynolds         // Note that control also reaches here via doPost and doDelete.
482b7981f6SKowalski, Kamil         auto session =
4955c7b7a2SEd Tanous             crow::persistent_data::SessionStore::getInstance().getSessionByUid(
504b1b8683SBorawski.Lukasz                 params[0]);
512b7981f6SKowalski, Kamil 
521abe55efSEd Tanous         if (session == nullptr)
531abe55efSEd Tanous         {
54f12894f8SJason M. Bills             messages::resourceNotFound(res, "Session", params[0]);
552b7981f6SKowalski, Kamil             res.end();
562b7981f6SKowalski, Kamil             return;
572b7981f6SKowalski, Kamil         }
582b7981f6SKowalski, Kamil 
590f74e643SEd Tanous         res.jsonValue["Id"] = session->uniqueId;
600f74e643SEd Tanous         res.jsonValue["UserName"] = session->username;
610f74e643SEd Tanous         res.jsonValue["@odata.id"] =
6255c7b7a2SEd Tanous             "/redfish/v1/SessionService/Sessions/" + session->uniqueId;
630f74e643SEd Tanous         res.jsonValue["@odata.type"] = "#Session.v1_0_2.Session";
640f74e643SEd Tanous         res.jsonValue["Name"] = "User Session";
650f74e643SEd Tanous         res.jsonValue["Description"] = "Manager User Session";
662b7981f6SKowalski, Kamil 
672b7981f6SKowalski, Kamil         res.end();
682b7981f6SKowalski, Kamil     }
692b7981f6SKowalski, Kamil 
7055c7b7a2SEd Tanous     void doDelete(crow::Response& res, const crow::Request& req,
711abe55efSEd Tanous                   const std::vector<std::string>& params) override
721abe55efSEd Tanous     {
732b7981f6SKowalski, Kamil         // Need only 1 param which should be id of session to be deleted
741abe55efSEd Tanous         if (params.size() != 1)
751abe55efSEd Tanous         {
76f4c4dcf4SKowalski, Kamil             // This should be handled by crow and never happen
771abe55efSEd Tanous             BMCWEB_LOG_ERROR << "Session DELETE has been called with invalid "
781abe55efSEd Tanous                                 "number of params";
79f4c4dcf4SKowalski, Kamil 
80f12894f8SJason M. Bills             messages::generalError(res);
812b7981f6SKowalski, Kamil             res.end();
822b7981f6SKowalski, Kamil             return;
832b7981f6SKowalski, Kamil         }
842b7981f6SKowalski, Kamil 
852b7981f6SKowalski, Kamil         auto session =
8655c7b7a2SEd Tanous             crow::persistent_data::SessionStore::getInstance().getSessionByUid(
874b1b8683SBorawski.Lukasz                 params[0]);
882b7981f6SKowalski, Kamil 
891abe55efSEd Tanous         if (session == nullptr)
901abe55efSEd Tanous         {
91f12894f8SJason M. Bills             messages::resourceNotFound(res, "Session", params[0]);
922b7981f6SKowalski, Kamil             res.end();
932b7981f6SKowalski, Kamil             return;
942b7981f6SKowalski, Kamil         }
952b7981f6SKowalski, Kamil 
96900f9497SJoseph Reynolds         // Perform a proper ConfigureSelf authority check.  If a
97900f9497SJoseph Reynolds         // session is being used to DELETE some other user's session,
98900f9497SJoseph Reynolds         // then the ConfigureSelf privilege does not apply.  In that
99900f9497SJoseph Reynolds         // case, perform the authority check again without the user's
100900f9497SJoseph Reynolds         // ConfigureSelf privilege.
101900f9497SJoseph Reynolds         if (session->username != req.session->username)
102900f9497SJoseph Reynolds         {
103900f9497SJoseph Reynolds             if (!isAllowedWithoutConfigureSelf(req))
104900f9497SJoseph Reynolds             {
105900f9497SJoseph Reynolds                 BMCWEB_LOG_WARNING << "DELETE Session denied access";
106900f9497SJoseph Reynolds                 messages::insufficientPrivilege(res);
107900f9497SJoseph Reynolds                 res.end();
108900f9497SJoseph Reynolds                 return;
109900f9497SJoseph Reynolds             }
110900f9497SJoseph Reynolds         }
111900f9497SJoseph Reynolds 
112f4c4dcf4SKowalski, Kamil         // DELETE should return representation of object that will be removed
113f4c4dcf4SKowalski, Kamil         doGet(res, req, params);
114f4c4dcf4SKowalski, Kamil 
1151abe55efSEd Tanous         crow::persistent_data::SessionStore::getInstance().removeSession(
1161abe55efSEd Tanous             session);
1172b7981f6SKowalski, Kamil     }
1182b7981f6SKowalski, Kamil 
1192b7981f6SKowalski, Kamil     /**
1202b7981f6SKowalski, Kamil      * This allows SessionCollection to reuse this class' doGet method, to
1211abe55efSEd Tanous      * maintain consistency of returned data, as Collection's doPost should
1221abe55efSEd Tanous      * return data for created member which should match member's doGet result
1231abe55efSEd Tanous      * in 100%
1242b7981f6SKowalski, Kamil      */
1252b7981f6SKowalski, Kamil     friend SessionCollection;
1262b7981f6SKowalski, Kamil };
1272b7981f6SKowalski, Kamil 
1281abe55efSEd Tanous class SessionCollection : public Node
1291abe55efSEd Tanous {
1302b7981f6SKowalski, Kamil   public:
1311abe55efSEd Tanous     SessionCollection(CrowApp& app) :
1321abe55efSEd Tanous         Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app)
1331abe55efSEd Tanous     {
134e0d918bcSEd Tanous         entityPrivileges = {
135e0d918bcSEd Tanous             {boost::beast::http::verb::get, {{"Login"}}},
136e0d918bcSEd Tanous             {boost::beast::http::verb::head, {{"Login"}}},
137e0d918bcSEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
138e0d918bcSEd Tanous             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
139e0d918bcSEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
140e0d918bcSEd Tanous             {boost::beast::http::verb::post, {}}};
1412b7981f6SKowalski, Kamil     }
1422b7981f6SKowalski, Kamil 
1432b7981f6SKowalski, Kamil   private:
14455c7b7a2SEd Tanous     void doGet(crow::Response& res, const crow::Request& req,
1451abe55efSEd Tanous                const std::vector<std::string>& params) override
1461abe55efSEd Tanous     {
14755c7b7a2SEd Tanous         std::vector<const std::string*> sessionIds =
14855c7b7a2SEd Tanous             crow::persistent_data::SessionStore::getInstance().getUniqueIds(
14955c7b7a2SEd Tanous                 false, crow::persistent_data::PersistenceType::TIMEOUT);
1502b7981f6SKowalski, Kamil 
1510f74e643SEd Tanous         res.jsonValue["Members@odata.count"] = sessionIds.size();
1520f74e643SEd Tanous         res.jsonValue["Members"] = nlohmann::json::array();
1531abe55efSEd Tanous         for (const std::string* uid : sessionIds)
1541abe55efSEd Tanous         {
1550f74e643SEd Tanous             res.jsonValue["Members"].push_back(
1562b7981f6SKowalski, Kamil                 {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}});
1572b7981f6SKowalski, Kamil         }
158f00032dbSTanous         res.jsonValue["Members@odata.count"] = sessionIds.size();
1590f74e643SEd Tanous         res.jsonValue["@odata.type"] = "#SessionCollection.SessionCollection";
1600f74e643SEd Tanous         res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/Sessions/";
1610f74e643SEd Tanous         res.jsonValue["Name"] = "Session Collection";
1620f74e643SEd Tanous         res.jsonValue["Description"] = "Session Collection";
1632b7981f6SKowalski, Kamil         res.end();
1642b7981f6SKowalski, Kamil     }
1652b7981f6SKowalski, Kamil 
16655c7b7a2SEd Tanous     void doPost(crow::Response& res, const crow::Request& req,
1671abe55efSEd Tanous                 const std::vector<std::string>& params) override
1681abe55efSEd Tanous     {
1699712f8acSEd Tanous         std::string username;
1709712f8acSEd Tanous         std::string password;
1719712f8acSEd Tanous         if (!json_util::readJson(req, res, "UserName", username, "Password",
1729712f8acSEd Tanous                                  password))
1731abe55efSEd Tanous         {
1742b7981f6SKowalski, Kamil             res.end();
1752b7981f6SKowalski, Kamil             return;
1762b7981f6SKowalski, Kamil         }
1772b7981f6SKowalski, Kamil 
178820ce598SEd Tanous         if (password.empty() || username.empty() ||
179820ce598SEd Tanous             res.result() != boost::beast::http::status::ok)
1801abe55efSEd Tanous         {
1811abe55efSEd Tanous             if (username.empty())
1821abe55efSEd Tanous             {
183a08b46ccSJason M. Bills                 messages::propertyMissing(res, "UserName");
184f4c4dcf4SKowalski, Kamil             }
185f4c4dcf4SKowalski, Kamil 
1861abe55efSEd Tanous             if (password.empty())
1871abe55efSEd Tanous             {
188a08b46ccSJason M. Bills                 messages::propertyMissing(res, "Password");
189820ce598SEd Tanous             }
190820ce598SEd Tanous             res.end();
191820ce598SEd Tanous 
192820ce598SEd Tanous             return;
193f4c4dcf4SKowalski, Kamil         }
1942b7981f6SKowalski, Kamil 
195*3bf4e632SJoseph Reynolds         int pamrc = pamAuthenticateUser(username, password);
196*3bf4e632SJoseph Reynolds         bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD;
197*3bf4e632SJoseph Reynolds         if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly)
1981abe55efSEd Tanous         {
199f12894f8SJason M. Bills             messages::resourceAtUriUnauthorized(res, std::string(req.url),
200f12894f8SJason M. Bills                                                 "Invalid username or password");
201820ce598SEd Tanous             res.end();
2022b7981f6SKowalski, Kamil 
203820ce598SEd Tanous             return;
2042b7981f6SKowalski, Kamil         }
2052b7981f6SKowalski, Kamil 
206820ce598SEd Tanous         // User is authenticated - create session
207820ce598SEd Tanous         std::shared_ptr<crow::persistent_data::UserSession> session =
208820ce598SEd Tanous             crow::persistent_data::SessionStore::getInstance()
209*3bf4e632SJoseph Reynolds                 .generateUserSession(
210*3bf4e632SJoseph Reynolds                     username, crow::persistent_data::PersistenceType::TIMEOUT,
211*3bf4e632SJoseph Reynolds                     isConfigureSelfOnly);
212820ce598SEd Tanous         res.addHeader("X-Auth-Token", session->sessionToken);
213820ce598SEd Tanous         res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" +
214820ce598SEd Tanous                                       session->uniqueId);
215820ce598SEd Tanous         res.result(boost::beast::http::status::created);
216*3bf4e632SJoseph Reynolds         if (session->isConfigureSelfOnly)
217*3bf4e632SJoseph Reynolds         {
218*3bf4e632SJoseph Reynolds             messages::passwordChangeRequired(
219*3bf4e632SJoseph Reynolds                 res,
220*3bf4e632SJoseph Reynolds                 "/redfish/v1/AccountService/Accounts/" + session->username);
221*3bf4e632SJoseph Reynolds         }
222820ce598SEd Tanous         memberSession.doGet(res, req, {session->uniqueId});
2232b7981f6SKowalski, Kamil     }
2242b7981f6SKowalski, Kamil 
2252b7981f6SKowalski, Kamil     /**
2262b7981f6SKowalski, Kamil      * Member session to ensure consistency between collection's doPost and
2272b7981f6SKowalski, Kamil      * member's doGet, as they should return 100% matching data
2282b7981f6SKowalski, Kamil      */
2292b7981f6SKowalski, Kamil     Sessions memberSession;
2302b7981f6SKowalski, Kamil };
2312b7981f6SKowalski, Kamil 
2321abe55efSEd Tanous class SessionService : public Node
2331abe55efSEd Tanous {
2345d27b854SBorawski.Lukasz   public:
2351abe55efSEd Tanous     SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/")
2361abe55efSEd Tanous     {
2373ebd75f7SEd Tanous 
238e0d918bcSEd Tanous         entityPrivileges = {
239e0d918bcSEd Tanous             {boost::beast::http::verb::get, {{"Login"}}},
240e0d918bcSEd Tanous             {boost::beast::http::verb::head, {{"Login"}}},
241e0d918bcSEd Tanous             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
242e0d918bcSEd Tanous             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
243e0d918bcSEd Tanous             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
244e0d918bcSEd Tanous             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2455d27b854SBorawski.Lukasz     }
2465d27b854SBorawski.Lukasz 
2475d27b854SBorawski.Lukasz   private:
24855c7b7a2SEd Tanous     void doGet(crow::Response& res, const crow::Request& req,
2491abe55efSEd Tanous                const std::vector<std::string>& params) override
2501abe55efSEd Tanous     {
2510f74e643SEd Tanous         res.jsonValue["@odata.type"] = "#SessionService.v1_0_2.SessionService";
2520f74e643SEd Tanous         res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/";
2530f74e643SEd Tanous         res.jsonValue["Name"] = "Session Service";
2540f74e643SEd Tanous         res.jsonValue["Id"] = "SessionService";
2550f74e643SEd Tanous         res.jsonValue["Description"] = "Session Service";
2560f74e643SEd Tanous         res.jsonValue["SessionTimeout"] =
2570f74e643SEd Tanous             crow::persistent_data::SessionStore::getInstance()
2580f74e643SEd Tanous                 .getTimeoutInSeconds();
2590f74e643SEd Tanous         res.jsonValue["ServiceEnabled"] = true;
2600f74e643SEd Tanous 
2610f261533SEd Tanous         res.jsonValue["Sessions"] = {
2620f261533SEd Tanous             {"@odata.id", "/redfish/v1/SessionService/Sessions"}};
2630f261533SEd Tanous 
2645d27b854SBorawski.Lukasz         res.end();
2655d27b854SBorawski.Lukasz     }
2665d27b854SBorawski.Lukasz };
2675d27b854SBorawski.Lukasz 
2682b7981f6SKowalski, Kamil } // namespace redfish
269