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 227e860f15SJohn Edward Broadbent #include <app.hpp> 237e860f15SJohn Edward Broadbent 241abe55efSEd Tanous namespace redfish 251abe55efSEd Tanous { 262b7981f6SKowalski, Kamil 272b7981f6SKowalski, Kamil class SessionCollection; 282b7981f6SKowalski, Kamil 291abe55efSEd Tanous class Sessions : public Node 301abe55efSEd Tanous { 312b7981f6SKowalski, Kamil public: 3252cc112dSEd Tanous Sessions(App& app) : 331abe55efSEd Tanous Node(app, "/redfish/v1/SessionService/Sessions/<str>/", std::string()) 341abe55efSEd Tanous { 35e0d918bcSEd Tanous entityPrivileges = { 36e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 37e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 38e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 39e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 40900f9497SJoseph Reynolds {boost::beast::http::verb::delete_, 41900f9497SJoseph Reynolds {{"ConfigureManager"}, {"ConfigureSelf"}}}, 42e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 432b7981f6SKowalski, Kamil } 442b7981f6SKowalski, Kamil 452b7981f6SKowalski, Kamil private: 468d1b46d7Szhanghch05 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 478d1b46d7Szhanghch05 const crow::Request&, 481abe55efSEd Tanous const std::vector<std::string>& params) override 491abe55efSEd Tanous { 50900f9497SJoseph Reynolds // Note that control also reaches here via doPost and doDelete. 512b7981f6SKowalski, Kamil auto session = 5252cc112dSEd Tanous persistent_data::SessionStore::getInstance().getSessionByUid( 534b1b8683SBorawski.Lukasz params[0]); 542b7981f6SKowalski, Kamil 551abe55efSEd Tanous if (session == nullptr) 561abe55efSEd Tanous { 578d1b46d7Szhanghch05 messages::resourceNotFound(asyncResp->res, "Session", params[0]); 582b7981f6SKowalski, Kamil return; 592b7981f6SKowalski, Kamil } 602b7981f6SKowalski, Kamil 618d1b46d7Szhanghch05 asyncResp->res.jsonValue["Id"] = session->uniqueId; 628d1b46d7Szhanghch05 asyncResp->res.jsonValue["UserName"] = session->username; 638d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = 6455c7b7a2SEd Tanous "/redfish/v1/SessionService/Sessions/" + session->uniqueId; 658d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = "#Session.v1_3_0.Session"; 668d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "User Session"; 678d1b46d7Szhanghch05 asyncResp->res.jsonValue["Description"] = "Manager User Session"; 688d1b46d7Szhanghch05 asyncResp->res.jsonValue["ClientOriginIPAddress"] = session->clientIp; 69c0ea7ae1SSunitha Harish #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE 708d1b46d7Szhanghch05 asyncResp->res.jsonValue["Oem"]["OpenBMC"]["@odata.type"] = 7108bdcc71SSunitha Harish "#OemSession.v1_0_0.Session"; 728d1b46d7Szhanghch05 asyncResp->res.jsonValue["Oem"]["OpenBMC"]["ClientID"] = 738d1b46d7Szhanghch05 session->clientId; 7408bdcc71SSunitha Harish #endif 752b7981f6SKowalski, Kamil } 762b7981f6SKowalski, Kamil 778d1b46d7Szhanghch05 void doDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 788d1b46d7Szhanghch05 const crow::Request& req, 791abe55efSEd Tanous const std::vector<std::string>& params) override 801abe55efSEd Tanous { 812b7981f6SKowalski, Kamil // Need only 1 param which should be id of session to be deleted 821abe55efSEd Tanous if (params.size() != 1) 831abe55efSEd Tanous { 84f4c4dcf4SKowalski, Kamil // This should be handled by crow and never happen 851abe55efSEd Tanous BMCWEB_LOG_ERROR << "Session DELETE has been called with invalid " 861abe55efSEd Tanous "number of params"; 87f4c4dcf4SKowalski, Kamil 888d1b46d7Szhanghch05 messages::generalError(asyncResp->res); 892b7981f6SKowalski, Kamil return; 902b7981f6SKowalski, Kamil } 912b7981f6SKowalski, Kamil 922b7981f6SKowalski, Kamil auto session = 9352cc112dSEd Tanous persistent_data::SessionStore::getInstance().getSessionByUid( 944b1b8683SBorawski.Lukasz params[0]); 952b7981f6SKowalski, Kamil 961abe55efSEd Tanous if (session == nullptr) 971abe55efSEd Tanous { 988d1b46d7Szhanghch05 messages::resourceNotFound(asyncResp->res, "Session", params[0]); 992b7981f6SKowalski, Kamil return; 1002b7981f6SKowalski, Kamil } 1012b7981f6SKowalski, Kamil 102900f9497SJoseph Reynolds // Perform a proper ConfigureSelf authority check. If a 103900f9497SJoseph Reynolds // session is being used to DELETE some other user's session, 104900f9497SJoseph Reynolds // then the ConfigureSelf privilege does not apply. In that 105900f9497SJoseph Reynolds // case, perform the authority check again without the user's 106900f9497SJoseph Reynolds // ConfigureSelf privilege. 107900f9497SJoseph Reynolds if (session->username != req.session->username) 108900f9497SJoseph Reynolds { 109*6c51eab1SEd Tanous Privileges effectiveUserPrivileges = 110*6c51eab1SEd Tanous redfish::getUserPrivileges(req.userRole); 111*6c51eab1SEd Tanous 112*6c51eab1SEd Tanous if (!effectiveUserPrivileges.isSupersetOf({{"ConfigureUsers"}})) 113900f9497SJoseph Reynolds { 1148d1b46d7Szhanghch05 messages::insufficientPrivilege(asyncResp->res); 115900f9497SJoseph Reynolds return; 116900f9497SJoseph Reynolds } 117900f9497SJoseph Reynolds } 118900f9497SJoseph Reynolds 119f4c4dcf4SKowalski, Kamil // DELETE should return representation of object that will be removed 1208d1b46d7Szhanghch05 doGet(asyncResp, req, params); 121f4c4dcf4SKowalski, Kamil 12252cc112dSEd Tanous persistent_data::SessionStore::getInstance().removeSession(session); 1232b7981f6SKowalski, Kamil } 1242b7981f6SKowalski, Kamil 1252b7981f6SKowalski, Kamil /** 1262b7981f6SKowalski, Kamil * This allows SessionCollection to reuse this class' doGet method, to 1271abe55efSEd Tanous * maintain consistency of returned data, as Collection's doPost should 12892f68223SSunitha Harish * return data for created member which should match member's doGet 12992f68223SSunitha Harish * result in 100% 1302b7981f6SKowalski, Kamil */ 1312b7981f6SKowalski, Kamil friend SessionCollection; 1322b7981f6SKowalski, Kamil }; 1332b7981f6SKowalski, Kamil 1341abe55efSEd Tanous class SessionCollection : public Node 1351abe55efSEd Tanous { 1362b7981f6SKowalski, Kamil public: 13752cc112dSEd Tanous SessionCollection(App& app) : 1381abe55efSEd Tanous Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) 1391abe55efSEd Tanous { 140e0d918bcSEd Tanous entityPrivileges = { 141e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 142e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 143e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 144e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 145e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 146e0d918bcSEd Tanous {boost::beast::http::verb::post, {}}}; 1472b7981f6SKowalski, Kamil } 1482b7981f6SKowalski, Kamil 1492b7981f6SKowalski, Kamil private: 1508d1b46d7Szhanghch05 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1518d1b46d7Szhanghch05 const crow::Request&, const std::vector<std::string>&) override 1521abe55efSEd Tanous { 15355c7b7a2SEd Tanous std::vector<const std::string*> sessionIds = 15452cc112dSEd Tanous persistent_data::SessionStore::getInstance().getUniqueIds( 15552cc112dSEd Tanous false, persistent_data::PersistenceType::TIMEOUT); 1562b7981f6SKowalski, Kamil 1578d1b46d7Szhanghch05 asyncResp->res.jsonValue["Members@odata.count"] = sessionIds.size(); 1588d1b46d7Szhanghch05 asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 1591abe55efSEd Tanous for (const std::string* uid : sessionIds) 1601abe55efSEd Tanous { 1618d1b46d7Szhanghch05 asyncResp->res.jsonValue["Members"].push_back( 1622b7981f6SKowalski, Kamil {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); 1632b7981f6SKowalski, Kamil } 1648d1b46d7Szhanghch05 asyncResp->res.jsonValue["Members@odata.count"] = sessionIds.size(); 1658d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 1668d1b46d7Szhanghch05 "#SessionCollection.SessionCollection"; 1678d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = 1688d1b46d7Szhanghch05 "/redfish/v1/SessionService/Sessions/"; 1698d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "Session Collection"; 1708d1b46d7Szhanghch05 asyncResp->res.jsonValue["Description"] = "Session Collection"; 1712b7981f6SKowalski, Kamil } 1722b7981f6SKowalski, Kamil 1738d1b46d7Szhanghch05 void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1748d1b46d7Szhanghch05 const crow::Request& req, 175cb13a392SEd Tanous const std::vector<std::string>&) override 1761abe55efSEd Tanous { 1779712f8acSEd Tanous std::string username; 1789712f8acSEd Tanous std::string password; 17908bdcc71SSunitha Harish std::optional<nlohmann::json> oemObject; 18008bdcc71SSunitha Harish std::string clientId; 1818d1b46d7Szhanghch05 if (!json_util::readJson(req, asyncResp->res, "UserName", username, 1828d1b46d7Szhanghch05 "Password", password, "Oem", oemObject)) 1831abe55efSEd Tanous { 1842b7981f6SKowalski, Kamil return; 1852b7981f6SKowalski, Kamil } 1862b7981f6SKowalski, Kamil 187820ce598SEd Tanous if (password.empty() || username.empty() || 1888d1b46d7Szhanghch05 asyncResp->res.result() != boost::beast::http::status::ok) 1891abe55efSEd Tanous { 1901abe55efSEd Tanous if (username.empty()) 1911abe55efSEd Tanous { 1928d1b46d7Szhanghch05 messages::propertyMissing(asyncResp->res, "UserName"); 193f4c4dcf4SKowalski, Kamil } 194f4c4dcf4SKowalski, Kamil 1951abe55efSEd Tanous if (password.empty()) 1961abe55efSEd Tanous { 1978d1b46d7Szhanghch05 messages::propertyMissing(asyncResp->res, "Password"); 198820ce598SEd Tanous } 199820ce598SEd Tanous 200820ce598SEd Tanous return; 201f4c4dcf4SKowalski, Kamil } 2022b7981f6SKowalski, Kamil 2033bf4e632SJoseph Reynolds int pamrc = pamAuthenticateUser(username, password); 2043bf4e632SJoseph Reynolds bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD; 2053bf4e632SJoseph Reynolds if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly) 2061abe55efSEd Tanous { 2078d1b46d7Szhanghch05 messages::resourceAtUriUnauthorized(asyncResp->res, 2088d1b46d7Szhanghch05 std::string(req.url), 209f12894f8SJason M. Bills "Invalid username or password"); 210820ce598SEd Tanous return; 2112b7981f6SKowalski, Kamil } 21208bdcc71SSunitha Harish #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE 21308bdcc71SSunitha Harish if (oemObject) 21408bdcc71SSunitha Harish { 21508bdcc71SSunitha Harish std::optional<nlohmann::json> bmcOem; 2168d1b46d7Szhanghch05 if (!json_util::readJson(*oemObject, asyncResp->res, "OpenBMC", 2178d1b46d7Szhanghch05 bmcOem)) 21808bdcc71SSunitha Harish { 21908bdcc71SSunitha Harish return; 22008bdcc71SSunitha Harish } 2218d1b46d7Szhanghch05 if (!json_util::readJson(*bmcOem, asyncResp->res, "ClientID", 2228d1b46d7Szhanghch05 clientId)) 22308bdcc71SSunitha Harish { 22408bdcc71SSunitha Harish BMCWEB_LOG_ERROR << "Could not read ClientId"; 22508bdcc71SSunitha Harish return; 22608bdcc71SSunitha Harish } 22708bdcc71SSunitha Harish } 22808bdcc71SSunitha Harish #endif 2296f115bbbSManojkiran Eda 230820ce598SEd Tanous // User is authenticated - create session 23152cc112dSEd Tanous std::shared_ptr<persistent_data::UserSession> session = 23252cc112dSEd Tanous persistent_data::SessionStore::getInstance().generateUserSession( 233d3239224SSunitha Harish username, req.ipAddress.to_string(), clientId, 234d3239224SSunitha Harish persistent_data::PersistenceType::TIMEOUT, isConfigureSelfOnly); 2358d1b46d7Szhanghch05 asyncResp->res.addHeader("X-Auth-Token", session->sessionToken); 2368d1b46d7Szhanghch05 asyncResp->res.addHeader("Location", 2378d1b46d7Szhanghch05 "/redfish/v1/SessionService/Sessions/" + 238820ce598SEd Tanous session->uniqueId); 2398d1b46d7Szhanghch05 asyncResp->res.result(boost::beast::http::status::created); 2403bf4e632SJoseph Reynolds if (session->isConfigureSelfOnly) 2413bf4e632SJoseph Reynolds { 2423bf4e632SJoseph Reynolds messages::passwordChangeRequired( 2438d1b46d7Szhanghch05 asyncResp->res, 2443bf4e632SJoseph Reynolds "/redfish/v1/AccountService/Accounts/" + session->username); 2453bf4e632SJoseph Reynolds } 2468d1b46d7Szhanghch05 memberSession.doGet(asyncResp, req, {session->uniqueId}); 2472b7981f6SKowalski, Kamil } 2482b7981f6SKowalski, Kamil 2492b7981f6SKowalski, Kamil /** 2502b7981f6SKowalski, Kamil * Member session to ensure consistency between collection's doPost and 2512b7981f6SKowalski, Kamil * member's doGet, as they should return 100% matching data 2522b7981f6SKowalski, Kamil */ 2532b7981f6SKowalski, Kamil Sessions memberSession; 2542b7981f6SKowalski, Kamil }; 2552b7981f6SKowalski, Kamil 2561abe55efSEd Tanous class SessionService : public Node 2571abe55efSEd Tanous { 2585d27b854SBorawski.Lukasz public: 25952cc112dSEd Tanous SessionService(App& app) : Node(app, "/redfish/v1/SessionService/") 2601abe55efSEd Tanous { 2613ebd75f7SEd Tanous 262e0d918bcSEd Tanous entityPrivileges = { 263e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 264e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 265e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 266e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 267e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 268e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 2695d27b854SBorawski.Lukasz } 2705d27b854SBorawski.Lukasz 2715d27b854SBorawski.Lukasz private: 2728d1b46d7Szhanghch05 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2738d1b46d7Szhanghch05 const crow::Request&, const std::vector<std::string>&) override 2741abe55efSEd Tanous { 2758d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 2768d1b46d7Szhanghch05 "#SessionService.v1_0_2.SessionService"; 2778d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/"; 2788d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "Session Service"; 2798d1b46d7Szhanghch05 asyncResp->res.jsonValue["Id"] = "SessionService"; 2808d1b46d7Szhanghch05 asyncResp->res.jsonValue["Description"] = "Session Service"; 2818d1b46d7Szhanghch05 asyncResp->res.jsonValue["SessionTimeout"] = 28252cc112dSEd Tanous persistent_data::SessionStore::getInstance().getTimeoutInSeconds(); 2838d1b46d7Szhanghch05 asyncResp->res.jsonValue["ServiceEnabled"] = true; 2840f74e643SEd Tanous 2858d1b46d7Szhanghch05 asyncResp->res.jsonValue["Sessions"] = { 2860f261533SEd Tanous {"@odata.id", "/redfish/v1/SessionService/Sessions"}}; 2875d27b854SBorawski.Lukasz } 288f2a4a606SManojkiran Eda 2898d1b46d7Szhanghch05 void doPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2908d1b46d7Szhanghch05 const crow::Request& req, 291f2a4a606SManojkiran Eda const std::vector<std::string>&) override 292f2a4a606SManojkiran Eda { 293f2a4a606SManojkiran Eda std::optional<int64_t> sessionTimeout; 2948d1b46d7Szhanghch05 if (!json_util::readJson(req, asyncResp->res, "SessionTimeout", 2958d1b46d7Szhanghch05 sessionTimeout)) 296f2a4a606SManojkiran Eda { 297f2a4a606SManojkiran Eda return; 298f2a4a606SManojkiran Eda } 299f2a4a606SManojkiran Eda 300f2a4a606SManojkiran Eda if (sessionTimeout) 301f2a4a606SManojkiran Eda { 302f2a4a606SManojkiran Eda // The mininum & maximum allowed values for session timeout are 30 303f2a4a606SManojkiran Eda // seconds and 86400 seconds respectively as per the session service 304f2a4a606SManojkiran Eda // schema mentioned at 305f2a4a606SManojkiran Eda // https://redfish.dmtf.org/schemas/v1/SessionService.v1_1_7.json 306f2a4a606SManojkiran Eda 307f2a4a606SManojkiran Eda if (*sessionTimeout <= 86400 && *sessionTimeout >= 30) 308f2a4a606SManojkiran Eda { 309f2a4a606SManojkiran Eda std::chrono::seconds sessionTimeoutInseconds(*sessionTimeout); 310f2a4a606SManojkiran Eda persistent_data::SessionStore::getInstance() 311f2a4a606SManojkiran Eda .updateSessionTimeout(sessionTimeoutInseconds); 312f2a4a606SManojkiran Eda messages::propertyValueModified( 313f2a4a606SManojkiran Eda asyncResp->res, "SessionTimeOut", 314f2a4a606SManojkiran Eda std::to_string(*sessionTimeout)); 315f2a4a606SManojkiran Eda } 316f2a4a606SManojkiran Eda else 317f2a4a606SManojkiran Eda { 318f2a4a606SManojkiran Eda messages::propertyValueNotInList( 3198d1b46d7Szhanghch05 asyncResp->res, std::to_string(*sessionTimeout), 3208d1b46d7Szhanghch05 "SessionTimeOut"); 321f2a4a606SManojkiran Eda } 322f2a4a606SManojkiran Eda } 323f2a4a606SManojkiran Eda } 3245d27b854SBorawski.Lukasz }; 3255d27b854SBorawski.Lukasz 3262b7981f6SKowalski, Kamil } // namespace redfish 327