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: 44cb13a392SEd Tanous void doGet(crow::Response& res, const crow::Request&, 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 = 4952cc112dSEd Tanous 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; 63c0ea7ae1SSunitha Harish res.jsonValue["@odata.type"] = "#Session.v1_3_0.Session"; 640f74e643SEd Tanous res.jsonValue["Name"] = "User Session"; 650f74e643SEd Tanous res.jsonValue["Description"] = "Manager User Session"; 66c0ea7ae1SSunitha Harish res.jsonValue["ClientOriginIPAddress"] = session->clientIp; 67c0ea7ae1SSunitha Harish #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE 6808bdcc71SSunitha Harish res.jsonValue["Oem"]["OpenBMC"]["@odata.type"] = 6908bdcc71SSunitha Harish "#OemSession.v1_0_0.Session"; 7008bdcc71SSunitha Harish res.jsonValue["Oem"]["OpenBMC"]["ClientID"] = session->clientId; 7108bdcc71SSunitha Harish #endif 722b7981f6SKowalski, Kamil res.end(); 732b7981f6SKowalski, Kamil } 742b7981f6SKowalski, Kamil 7555c7b7a2SEd Tanous void doDelete(crow::Response& res, const crow::Request& req, 761abe55efSEd Tanous const std::vector<std::string>& params) override 771abe55efSEd Tanous { 782b7981f6SKowalski, Kamil // Need only 1 param which should be id of session to be deleted 791abe55efSEd Tanous if (params.size() != 1) 801abe55efSEd Tanous { 81f4c4dcf4SKowalski, Kamil // This should be handled by crow and never happen 821abe55efSEd Tanous BMCWEB_LOG_ERROR << "Session DELETE has been called with invalid " 831abe55efSEd Tanous "number of params"; 84f4c4dcf4SKowalski, Kamil 85f12894f8SJason M. Bills messages::generalError(res); 862b7981f6SKowalski, Kamil res.end(); 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 { 96f12894f8SJason M. Bills messages::resourceNotFound(res, "Session", params[0]); 972b7981f6SKowalski, Kamil res.end(); 982b7981f6SKowalski, Kamil return; 992b7981f6SKowalski, Kamil } 1002b7981f6SKowalski, Kamil 101900f9497SJoseph Reynolds // Perform a proper ConfigureSelf authority check. If a 102900f9497SJoseph Reynolds // session is being used to DELETE some other user's session, 103900f9497SJoseph Reynolds // then the ConfigureSelf privilege does not apply. In that 104900f9497SJoseph Reynolds // case, perform the authority check again without the user's 105900f9497SJoseph Reynolds // ConfigureSelf privilege. 106900f9497SJoseph Reynolds if (session->username != req.session->username) 107900f9497SJoseph Reynolds { 108900f9497SJoseph Reynolds if (!isAllowedWithoutConfigureSelf(req)) 109900f9497SJoseph Reynolds { 110900f9497SJoseph Reynolds BMCWEB_LOG_WARNING << "DELETE Session denied access"; 111900f9497SJoseph Reynolds messages::insufficientPrivilege(res); 112900f9497SJoseph Reynolds res.end(); 113900f9497SJoseph Reynolds return; 114900f9497SJoseph Reynolds } 115900f9497SJoseph Reynolds } 116900f9497SJoseph Reynolds 117f4c4dcf4SKowalski, Kamil // DELETE should return representation of object that will be removed 118f4c4dcf4SKowalski, Kamil doGet(res, req, params); 119f4c4dcf4SKowalski, Kamil 12052cc112dSEd Tanous persistent_data::SessionStore::getInstance().removeSession(session); 1212b7981f6SKowalski, Kamil } 1222b7981f6SKowalski, Kamil 1232b7981f6SKowalski, Kamil /** 1242b7981f6SKowalski, Kamil * This allows SessionCollection to reuse this class' doGet method, to 1251abe55efSEd Tanous * maintain consistency of returned data, as Collection's doPost should 12692f68223SSunitha Harish * return data for created member which should match member's doGet 12792f68223SSunitha Harish * result in 100% 1282b7981f6SKowalski, Kamil */ 1292b7981f6SKowalski, Kamil friend SessionCollection; 1302b7981f6SKowalski, Kamil }; 1312b7981f6SKowalski, Kamil 1321abe55efSEd Tanous class SessionCollection : public Node 1331abe55efSEd Tanous { 1342b7981f6SKowalski, Kamil public: 13552cc112dSEd Tanous SessionCollection(App& app) : 1361abe55efSEd Tanous Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) 1371abe55efSEd Tanous { 138e0d918bcSEd Tanous entityPrivileges = { 139e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 140e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 141e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 142e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 143e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 144e0d918bcSEd Tanous {boost::beast::http::verb::post, {}}}; 1452b7981f6SKowalski, Kamil } 1462b7981f6SKowalski, Kamil 1472b7981f6SKowalski, Kamil private: 148cb13a392SEd Tanous void doGet(crow::Response& res, const crow::Request&, 149cb13a392SEd Tanous const std::vector<std::string>&) override 1501abe55efSEd Tanous { 15155c7b7a2SEd Tanous std::vector<const std::string*> sessionIds = 15252cc112dSEd Tanous persistent_data::SessionStore::getInstance().getUniqueIds( 15352cc112dSEd Tanous false, persistent_data::PersistenceType::TIMEOUT); 1542b7981f6SKowalski, Kamil 1550f74e643SEd Tanous res.jsonValue["Members@odata.count"] = sessionIds.size(); 1560f74e643SEd Tanous res.jsonValue["Members"] = nlohmann::json::array(); 1571abe55efSEd Tanous for (const std::string* uid : sessionIds) 1581abe55efSEd Tanous { 1590f74e643SEd Tanous res.jsonValue["Members"].push_back( 1602b7981f6SKowalski, Kamil {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); 1612b7981f6SKowalski, Kamil } 162f00032dbSTanous res.jsonValue["Members@odata.count"] = sessionIds.size(); 1630f74e643SEd Tanous res.jsonValue["@odata.type"] = "#SessionCollection.SessionCollection"; 1640f74e643SEd Tanous res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; 1650f74e643SEd Tanous res.jsonValue["Name"] = "Session Collection"; 1660f74e643SEd Tanous res.jsonValue["Description"] = "Session Collection"; 1672b7981f6SKowalski, Kamil res.end(); 1682b7981f6SKowalski, Kamil } 1692b7981f6SKowalski, Kamil 17055c7b7a2SEd Tanous void doPost(crow::Response& res, 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; 1779712f8acSEd Tanous if (!json_util::readJson(req, res, "UserName", username, "Password", 17808bdcc71SSunitha Harish password, "Oem", oemObject)) 1791abe55efSEd Tanous { 1802b7981f6SKowalski, Kamil res.end(); 1812b7981f6SKowalski, Kamil return; 1822b7981f6SKowalski, Kamil } 1832b7981f6SKowalski, Kamil 184820ce598SEd Tanous if (password.empty() || username.empty() || 185820ce598SEd Tanous res.result() != boost::beast::http::status::ok) 1861abe55efSEd Tanous { 1871abe55efSEd Tanous if (username.empty()) 1881abe55efSEd Tanous { 189a08b46ccSJason M. Bills messages::propertyMissing(res, "UserName"); 190f4c4dcf4SKowalski, Kamil } 191f4c4dcf4SKowalski, Kamil 1921abe55efSEd Tanous if (password.empty()) 1931abe55efSEd Tanous { 194a08b46ccSJason M. Bills messages::propertyMissing(res, "Password"); 195820ce598SEd Tanous } 196820ce598SEd Tanous res.end(); 197820ce598SEd Tanous 198820ce598SEd Tanous return; 199f4c4dcf4SKowalski, Kamil } 2002b7981f6SKowalski, Kamil 2013bf4e632SJoseph Reynolds int pamrc = pamAuthenticateUser(username, password); 2023bf4e632SJoseph Reynolds bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD; 2033bf4e632SJoseph Reynolds if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly) 2041abe55efSEd Tanous { 205f12894f8SJason M. Bills messages::resourceAtUriUnauthorized(res, std::string(req.url), 206f12894f8SJason M. Bills "Invalid username or password"); 207820ce598SEd Tanous res.end(); 2082b7981f6SKowalski, Kamil 209820ce598SEd Tanous return; 2102b7981f6SKowalski, Kamil } 21108bdcc71SSunitha Harish #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE 21208bdcc71SSunitha Harish if (oemObject) 21308bdcc71SSunitha Harish { 21408bdcc71SSunitha Harish std::optional<nlohmann::json> bmcOem; 21508bdcc71SSunitha Harish if (!json_util::readJson(*oemObject, res, "OpenBMC", bmcOem)) 21608bdcc71SSunitha Harish { 21708bdcc71SSunitha Harish res.end(); 21808bdcc71SSunitha Harish return; 21908bdcc71SSunitha Harish } 22008bdcc71SSunitha Harish if (!json_util::readJson(*bmcOem, res, "ClientID", clientId)) 22108bdcc71SSunitha Harish { 22208bdcc71SSunitha Harish BMCWEB_LOG_ERROR << "Could not read ClientId"; 22308bdcc71SSunitha Harish res.end(); 22408bdcc71SSunitha Harish return; 22508bdcc71SSunitha Harish } 22608bdcc71SSunitha Harish } 22708bdcc71SSunitha Harish #endif 2286f115bbbSManojkiran Eda 229820ce598SEd Tanous // User is authenticated - create session 23052cc112dSEd Tanous std::shared_ptr<persistent_data::UserSession> session = 23152cc112dSEd Tanous persistent_data::SessionStore::getInstance().generateUserSession( 232*d3239224SSunitha Harish username, req.ipAddress.to_string(), clientId, 233*d3239224SSunitha Harish persistent_data::PersistenceType::TIMEOUT, isConfigureSelfOnly); 234820ce598SEd Tanous res.addHeader("X-Auth-Token", session->sessionToken); 235820ce598SEd Tanous res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" + 236820ce598SEd Tanous session->uniqueId); 237820ce598SEd Tanous res.result(boost::beast::http::status::created); 2383bf4e632SJoseph Reynolds if (session->isConfigureSelfOnly) 2393bf4e632SJoseph Reynolds { 2403bf4e632SJoseph Reynolds messages::passwordChangeRequired( 2413bf4e632SJoseph Reynolds res, 2423bf4e632SJoseph Reynolds "/redfish/v1/AccountService/Accounts/" + session->username); 2433bf4e632SJoseph Reynolds } 244820ce598SEd Tanous memberSession.doGet(res, req, {session->uniqueId}); 2452b7981f6SKowalski, Kamil } 2462b7981f6SKowalski, Kamil 2472b7981f6SKowalski, Kamil /** 2482b7981f6SKowalski, Kamil * Member session to ensure consistency between collection's doPost and 2492b7981f6SKowalski, Kamil * member's doGet, as they should return 100% matching data 2502b7981f6SKowalski, Kamil */ 2512b7981f6SKowalski, Kamil Sessions memberSession; 2522b7981f6SKowalski, Kamil }; 2532b7981f6SKowalski, Kamil 2541abe55efSEd Tanous class SessionService : public Node 2551abe55efSEd Tanous { 2565d27b854SBorawski.Lukasz public: 25752cc112dSEd Tanous SessionService(App& app) : Node(app, "/redfish/v1/SessionService/") 2581abe55efSEd Tanous { 2593ebd75f7SEd Tanous 260e0d918bcSEd Tanous entityPrivileges = { 261e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 262e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 263e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 264e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 265e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 266e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 2675d27b854SBorawski.Lukasz } 2685d27b854SBorawski.Lukasz 2695d27b854SBorawski.Lukasz private: 270cb13a392SEd Tanous void doGet(crow::Response& res, const crow::Request&, 271cb13a392SEd Tanous const std::vector<std::string>&) override 2721abe55efSEd Tanous { 2730f74e643SEd Tanous res.jsonValue["@odata.type"] = "#SessionService.v1_0_2.SessionService"; 2740f74e643SEd Tanous res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/"; 2750f74e643SEd Tanous res.jsonValue["Name"] = "Session Service"; 2760f74e643SEd Tanous res.jsonValue["Id"] = "SessionService"; 2770f74e643SEd Tanous res.jsonValue["Description"] = "Session Service"; 2780f74e643SEd Tanous res.jsonValue["SessionTimeout"] = 27952cc112dSEd Tanous persistent_data::SessionStore::getInstance().getTimeoutInSeconds(); 2800f74e643SEd Tanous res.jsonValue["ServiceEnabled"] = true; 2810f74e643SEd Tanous 2820f261533SEd Tanous res.jsonValue["Sessions"] = { 2830f261533SEd Tanous {"@odata.id", "/redfish/v1/SessionService/Sessions"}}; 2840f261533SEd Tanous 2855d27b854SBorawski.Lukasz res.end(); 2865d27b854SBorawski.Lukasz } 287f2a4a606SManojkiran Eda 288f2a4a606SManojkiran Eda void doPatch(crow::Response& res, const crow::Request& req, 289f2a4a606SManojkiran Eda const std::vector<std::string>&) override 290f2a4a606SManojkiran Eda { 291f2a4a606SManojkiran Eda std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 292f2a4a606SManojkiran Eda std::optional<int64_t> sessionTimeout; 293f2a4a606SManojkiran Eda if (!json_util::readJson(req, res, "SessionTimeout", sessionTimeout)) 294f2a4a606SManojkiran Eda { 295f2a4a606SManojkiran Eda return; 296f2a4a606SManojkiran Eda } 297f2a4a606SManojkiran Eda 298f2a4a606SManojkiran Eda if (sessionTimeout) 299f2a4a606SManojkiran Eda { 300f2a4a606SManojkiran Eda // The mininum & maximum allowed values for session timeout are 30 301f2a4a606SManojkiran Eda // seconds and 86400 seconds respectively as per the session service 302f2a4a606SManojkiran Eda // schema mentioned at 303f2a4a606SManojkiran Eda // https://redfish.dmtf.org/schemas/v1/SessionService.v1_1_7.json 304f2a4a606SManojkiran Eda 305f2a4a606SManojkiran Eda if (*sessionTimeout <= 86400 && *sessionTimeout >= 30) 306f2a4a606SManojkiran Eda { 307f2a4a606SManojkiran Eda std::chrono::seconds sessionTimeoutInseconds(*sessionTimeout); 308f2a4a606SManojkiran Eda persistent_data::SessionStore::getInstance() 309f2a4a606SManojkiran Eda .updateSessionTimeout(sessionTimeoutInseconds); 310f2a4a606SManojkiran Eda messages::propertyValueModified( 311f2a4a606SManojkiran Eda asyncResp->res, "SessionTimeOut", 312f2a4a606SManojkiran Eda std::to_string(*sessionTimeout)); 313f2a4a606SManojkiran Eda } 314f2a4a606SManojkiran Eda else 315f2a4a606SManojkiran Eda { 316f2a4a606SManojkiran Eda messages::propertyValueNotInList( 317f2a4a606SManojkiran Eda res, std::to_string(*sessionTimeout), "SessionTimeOut"); 318f2a4a606SManojkiran Eda } 319f2a4a606SManojkiran Eda } 320f2a4a606SManojkiran Eda } 3215d27b854SBorawski.Lukasz }; 3225d27b854SBorawski.Lukasz 3232b7981f6SKowalski, Kamil } // namespace redfish 324