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