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