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" 1952cc112dSEd Tanous #include "persistent_data.hpp" 202b7981f6SKowalski, Kamil 217e860f15SJohn Edward Broadbent #include <app.hpp> 227e860f15SJohn Edward Broadbent 231abe55efSEd Tanous namespace redfish 241abe55efSEd Tanous { 252b7981f6SKowalski, Kamil 262b7981f6SKowalski, Kamil class SessionCollection; 272b7981f6SKowalski, Kamil 28faa34ccfSEd Tanous void fillSessionObject(crow::Response& res, 29faa34ccfSEd Tanous const persistent_data::UserSession& session) 301abe55efSEd Tanous { 31faa34ccfSEd Tanous res.jsonValue["Id"] = session.uniqueId; 32faa34ccfSEd Tanous res.jsonValue["UserName"] = session.username; 33faa34ccfSEd Tanous res.jsonValue["@odata.id"] = 34faa34ccfSEd Tanous "/redfish/v1/SessionService/Sessions/" + session.uniqueId; 35faa34ccfSEd Tanous res.jsonValue["@odata.type"] = "#Session.v1_3_0.Session"; 36faa34ccfSEd Tanous res.jsonValue["Name"] = "User Session"; 37faa34ccfSEd Tanous res.jsonValue["Description"] = "Manager User Session"; 38faa34ccfSEd Tanous res.jsonValue["ClientOriginIPAddress"] = session.clientIp; 39c0ea7ae1SSunitha Harish #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE 40faa34ccfSEd Tanous res.jsonValue["Oem"]["OpenBMC"]["@odata.type"] = 4108bdcc71SSunitha Harish "#OemSession.v1_0_0.Session"; 42faa34ccfSEd Tanous res.jsonValue["Oem"]["OpenBMC"]["ClientID"] = session.clientId; 4308bdcc71SSunitha Harish #endif 442b7981f6SKowalski, Kamil } 452b7981f6SKowalski, Kamil 46faa34ccfSEd Tanous inline void requestRoutesSession(App& app) 471abe55efSEd Tanous { 48faa34ccfSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/") 49faa34ccfSEd Tanous .privileges({{"Login"}}) 50faa34ccfSEd Tanous .methods(boost::beast::http::verb::get)( 51faa34ccfSEd Tanous [](const crow::Request& /*req*/, 52faa34ccfSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 53faa34ccfSEd Tanous const std::string& sessionId) -> void { 54faa34ccfSEd Tanous // Note that control also reaches here via doPost and doDelete. 55faa34ccfSEd Tanous auto session = persistent_data::SessionStore::getInstance() 56faa34ccfSEd Tanous .getSessionByUid(sessionId); 572b7981f6SKowalski, Kamil 581abe55efSEd Tanous if (session == nullptr) 591abe55efSEd Tanous { 60faa34ccfSEd Tanous messages::resourceNotFound(asyncResp->res, "Session", 61faa34ccfSEd Tanous sessionId); 62faa34ccfSEd Tanous return; 63faa34ccfSEd Tanous } 64faa34ccfSEd Tanous 65faa34ccfSEd Tanous fillSessionObject(asyncResp->res, *session); 66faa34ccfSEd Tanous }); 67faa34ccfSEd Tanous 68faa34ccfSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/<str>/") 69faa34ccfSEd Tanous .privileges({{"ConfigureManager"}, {"ConfigureSelf"}}) 70faa34ccfSEd Tanous .methods(boost::beast::http::verb::delete_)( 71faa34ccfSEd Tanous [](const crow::Request& req, 72faa34ccfSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 73faa34ccfSEd Tanous const std::string& sessionId) -> void { 74faa34ccfSEd Tanous auto session = persistent_data::SessionStore::getInstance() 75faa34ccfSEd Tanous .getSessionByUid(sessionId); 76faa34ccfSEd Tanous 77faa34ccfSEd Tanous if (session == nullptr) 78faa34ccfSEd Tanous { 79faa34ccfSEd Tanous messages::resourceNotFound(asyncResp->res, "Session", 80faa34ccfSEd Tanous sessionId); 812b7981f6SKowalski, Kamil return; 822b7981f6SKowalski, Kamil } 832b7981f6SKowalski, Kamil 84900f9497SJoseph Reynolds // Perform a proper ConfigureSelf authority check. If a 85900f9497SJoseph Reynolds // session is being used to DELETE some other user's session, 86900f9497SJoseph Reynolds // then the ConfigureSelf privilege does not apply. In that 87900f9497SJoseph Reynolds // case, perform the authority check again without the user's 88900f9497SJoseph Reynolds // ConfigureSelf privilege. 89900f9497SJoseph Reynolds if (session->username != req.session->username) 90900f9497SJoseph Reynolds { 916c51eab1SEd Tanous Privileges effectiveUserPrivileges = 926c51eab1SEd Tanous redfish::getUserPrivileges(req.userRole); 936c51eab1SEd Tanous 94faa34ccfSEd Tanous if (!effectiveUserPrivileges.isSupersetOf( 95faa34ccfSEd Tanous {{"ConfigureUsers"}})) 96900f9497SJoseph Reynolds { 978d1b46d7Szhanghch05 messages::insufficientPrivilege(asyncResp->res); 98900f9497SJoseph Reynolds return; 99900f9497SJoseph Reynolds } 100900f9497SJoseph Reynolds } 101900f9497SJoseph Reynolds 102faa34ccfSEd Tanous persistent_data::SessionStore::getInstance().removeSession( 103faa34ccfSEd Tanous session); 104*5cc148afSEd Tanous messages::success(asyncResp->res); 105faa34ccfSEd Tanous }); 106f4c4dcf4SKowalski, Kamil 107faa34ccfSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/") 108faa34ccfSEd Tanous .privileges({{"Login"}}) 109faa34ccfSEd Tanous .methods(boost::beast::http::verb::get)( 110faa34ccfSEd Tanous [](const crow::Request& /*req*/, 111faa34ccfSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void { 11255c7b7a2SEd Tanous std::vector<const std::string*> sessionIds = 11352cc112dSEd Tanous persistent_data::SessionStore::getInstance().getUniqueIds( 11452cc112dSEd Tanous false, persistent_data::PersistenceType::TIMEOUT); 1152b7981f6SKowalski, Kamil 116faa34ccfSEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = 117faa34ccfSEd Tanous sessionIds.size(); 1188d1b46d7Szhanghch05 asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 1191abe55efSEd Tanous for (const std::string* uid : sessionIds) 1201abe55efSEd Tanous { 1218d1b46d7Szhanghch05 asyncResp->res.jsonValue["Members"].push_back( 122faa34ccfSEd Tanous {{"@odata.id", 123faa34ccfSEd Tanous "/redfish/v1/SessionService/Sessions/" + *uid}}); 1242b7981f6SKowalski, Kamil } 125faa34ccfSEd Tanous asyncResp->res.jsonValue["Members@odata.count"] = 126faa34ccfSEd Tanous sessionIds.size(); 1278d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 1288d1b46d7Szhanghch05 "#SessionCollection.SessionCollection"; 1298d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = 1308d1b46d7Szhanghch05 "/redfish/v1/SessionService/Sessions/"; 1318d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "Session Collection"; 1328d1b46d7Szhanghch05 asyncResp->res.jsonValue["Description"] = "Session Collection"; 133faa34ccfSEd Tanous }); 1342b7981f6SKowalski, Kamil 135faa34ccfSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/SessionService/Sessions/") 136faa34ccfSEd Tanous .privileges({}) 137faa34ccfSEd Tanous .methods(boost::beast::http::verb::post)( 138faa34ccfSEd Tanous [](const crow::Request& req, 139faa34ccfSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void { 1409712f8acSEd Tanous std::string username; 1419712f8acSEd Tanous std::string password; 14208bdcc71SSunitha Harish std::optional<nlohmann::json> oemObject; 14308bdcc71SSunitha Harish std::string clientId; 144faa34ccfSEd Tanous if (!json_util::readJson(req, asyncResp->res, "UserName", 145faa34ccfSEd Tanous username, "Password", password, "Oem", 146faa34ccfSEd Tanous oemObject)) 1471abe55efSEd Tanous { 1482b7981f6SKowalski, Kamil return; 1492b7981f6SKowalski, Kamil } 1502b7981f6SKowalski, Kamil 151820ce598SEd Tanous if (password.empty() || username.empty() || 1528d1b46d7Szhanghch05 asyncResp->res.result() != boost::beast::http::status::ok) 1531abe55efSEd Tanous { 1541abe55efSEd Tanous if (username.empty()) 1551abe55efSEd Tanous { 1568d1b46d7Szhanghch05 messages::propertyMissing(asyncResp->res, "UserName"); 157f4c4dcf4SKowalski, Kamil } 158f4c4dcf4SKowalski, Kamil 1591abe55efSEd Tanous if (password.empty()) 1601abe55efSEd Tanous { 1618d1b46d7Szhanghch05 messages::propertyMissing(asyncResp->res, "Password"); 162820ce598SEd Tanous } 163820ce598SEd Tanous 164820ce598SEd Tanous return; 165f4c4dcf4SKowalski, Kamil } 1662b7981f6SKowalski, Kamil 1673bf4e632SJoseph Reynolds int pamrc = pamAuthenticateUser(username, password); 1683bf4e632SJoseph Reynolds bool isConfigureSelfOnly = pamrc == PAM_NEW_AUTHTOK_REQD; 1693bf4e632SJoseph Reynolds if ((pamrc != PAM_SUCCESS) && !isConfigureSelfOnly) 1701abe55efSEd Tanous { 171faa34ccfSEd Tanous messages::resourceAtUriUnauthorized( 172faa34ccfSEd Tanous asyncResp->res, std::string(req.url), 173f12894f8SJason M. Bills "Invalid username or password"); 174820ce598SEd Tanous return; 1752b7981f6SKowalski, Kamil } 17608bdcc71SSunitha Harish #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE 17708bdcc71SSunitha Harish if (oemObject) 17808bdcc71SSunitha Harish { 17908bdcc71SSunitha Harish std::optional<nlohmann::json> bmcOem; 180faa34ccfSEd Tanous if (!json_util::readJson(*oemObject, asyncResp->res, 181faa34ccfSEd Tanous "OpenBMC", bmcOem)) 18208bdcc71SSunitha Harish { 18308bdcc71SSunitha Harish return; 18408bdcc71SSunitha Harish } 185faa34ccfSEd Tanous if (!json_util::readJson(*bmcOem, asyncResp->res, 186faa34ccfSEd Tanous "ClientID", clientId)) 18708bdcc71SSunitha Harish { 18808bdcc71SSunitha Harish BMCWEB_LOG_ERROR << "Could not read ClientId"; 18908bdcc71SSunitha Harish return; 19008bdcc71SSunitha Harish } 19108bdcc71SSunitha Harish } 19208bdcc71SSunitha Harish #endif 1936f115bbbSManojkiran Eda 194820ce598SEd Tanous // User is authenticated - create session 19552cc112dSEd Tanous std::shared_ptr<persistent_data::UserSession> session = 196faa34ccfSEd Tanous persistent_data::SessionStore::getInstance() 197faa34ccfSEd Tanous .generateUserSession( 198d3239224SSunitha Harish username, req.ipAddress.to_string(), clientId, 199faa34ccfSEd Tanous persistent_data::PersistenceType::TIMEOUT, 200faa34ccfSEd Tanous isConfigureSelfOnly); 2018d1b46d7Szhanghch05 asyncResp->res.addHeader("X-Auth-Token", session->sessionToken); 202faa34ccfSEd Tanous asyncResp->res.addHeader( 203faa34ccfSEd Tanous "Location", 204faa34ccfSEd Tanous "/redfish/v1/SessionService/Sessions/" + session->uniqueId); 2058d1b46d7Szhanghch05 asyncResp->res.result(boost::beast::http::status::created); 2063bf4e632SJoseph Reynolds if (session->isConfigureSelfOnly) 2073bf4e632SJoseph Reynolds { 2083bf4e632SJoseph Reynolds messages::passwordChangeRequired( 209faa34ccfSEd Tanous asyncResp->res, "/redfish/v1/AccountService/Accounts/" + 210faa34ccfSEd Tanous session->username); 2112b7981f6SKowalski, Kamil } 2122b7981f6SKowalski, Kamil 213faa34ccfSEd Tanous fillSessionObject(asyncResp->res, *session); 214faa34ccfSEd Tanous }); 2152b7981f6SKowalski, Kamil 216faa34ccfSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/SessionService/") 217faa34ccfSEd Tanous .privileges({{"Login"}}) 218faa34ccfSEd Tanous .methods(boost::beast::http::verb::get)( 219faa34ccfSEd Tanous [](const crow::Request& /* req */, 220faa34ccfSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void { 2218d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 2228d1b46d7Szhanghch05 "#SessionService.v1_0_2.SessionService"; 223faa34ccfSEd Tanous asyncResp->res.jsonValue["@odata.id"] = 224faa34ccfSEd Tanous "/redfish/v1/SessionService/"; 2258d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "Session Service"; 2268d1b46d7Szhanghch05 asyncResp->res.jsonValue["Id"] = "SessionService"; 2278d1b46d7Szhanghch05 asyncResp->res.jsonValue["Description"] = "Session Service"; 2288d1b46d7Szhanghch05 asyncResp->res.jsonValue["SessionTimeout"] = 229faa34ccfSEd Tanous persistent_data::SessionStore::getInstance() 230faa34ccfSEd Tanous .getTimeoutInSeconds(); 2318d1b46d7Szhanghch05 asyncResp->res.jsonValue["ServiceEnabled"] = true; 2320f74e643SEd Tanous 2338d1b46d7Szhanghch05 asyncResp->res.jsonValue["Sessions"] = { 2340f261533SEd Tanous {"@odata.id", "/redfish/v1/SessionService/Sessions"}}; 235faa34ccfSEd Tanous }); 236f2a4a606SManojkiran Eda 237faa34ccfSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/SessionService/") 238faa34ccfSEd Tanous .privileges({{"ConfigureManager"}}) 239faa34ccfSEd Tanous .methods(boost::beast::http::verb::patch)( 240faa34ccfSEd Tanous [](const crow::Request& req, 241faa34ccfSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) -> void { 242f2a4a606SManojkiran Eda std::optional<int64_t> sessionTimeout; 2438d1b46d7Szhanghch05 if (!json_util::readJson(req, asyncResp->res, "SessionTimeout", 2448d1b46d7Szhanghch05 sessionTimeout)) 245f2a4a606SManojkiran Eda { 246f2a4a606SManojkiran Eda return; 247f2a4a606SManojkiran Eda } 248f2a4a606SManojkiran Eda 249f2a4a606SManojkiran Eda if (sessionTimeout) 250f2a4a606SManojkiran Eda { 251faa34ccfSEd Tanous // The mininum & maximum allowed values for session timeout 252faa34ccfSEd Tanous // are 30 seconds and 86400 seconds respectively as per the 253faa34ccfSEd Tanous // session service schema mentioned at 254f2a4a606SManojkiran Eda // https://redfish.dmtf.org/schemas/v1/SessionService.v1_1_7.json 255f2a4a606SManojkiran Eda 256f2a4a606SManojkiran Eda if (*sessionTimeout <= 86400 && *sessionTimeout >= 30) 257f2a4a606SManojkiran Eda { 258faa34ccfSEd Tanous std::chrono::seconds sessionTimeoutInseconds( 259faa34ccfSEd Tanous *sessionTimeout); 260f2a4a606SManojkiran Eda persistent_data::SessionStore::getInstance() 261f2a4a606SManojkiran Eda .updateSessionTimeout(sessionTimeoutInseconds); 262f2a4a606SManojkiran Eda messages::propertyValueModified( 263f2a4a606SManojkiran Eda asyncResp->res, "SessionTimeOut", 264f2a4a606SManojkiran Eda std::to_string(*sessionTimeout)); 265f2a4a606SManojkiran Eda } 266f2a4a606SManojkiran Eda else 267f2a4a606SManojkiran Eda { 268f2a4a606SManojkiran Eda messages::propertyValueNotInList( 2698d1b46d7Szhanghch05 asyncResp->res, std::to_string(*sessionTimeout), 2708d1b46d7Szhanghch05 "SessionTimeOut"); 271f2a4a606SManojkiran Eda } 272f2a4a606SManojkiran Eda } 273faa34ccfSEd Tanous }); 274f2a4a606SManojkiran Eda } 2755d27b854SBorawski.Lukasz 2762b7981f6SKowalski, Kamil } // namespace redfish 277