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 { 33c1a46bd2SBorawski.Lukasz Node::json["@odata.type"] = "#Session.v1_0_2.Session"; 34c1a46bd2SBorawski.Lukasz Node::json["@odata.context"] = "/redfish/v1/$metadata#Session.Session"; 35c1a46bd2SBorawski.Lukasz Node::json["Name"] = "User Session"; 36c1a46bd2SBorawski.Lukasz Node::json["Description"] = "Manager User Session"; 373ebd75f7SEd Tanous 38e0d918bcSEd Tanous entityPrivileges = { 39e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 40e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 41e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 42e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 43e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 44e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 452b7981f6SKowalski, Kamil } 462b7981f6SKowalski, Kamil 472b7981f6SKowalski, Kamil private: 4855c7b7a2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 491abe55efSEd Tanous const std::vector<std::string>& params) override 501abe55efSEd Tanous { 512b7981f6SKowalski, Kamil auto session = 5255c7b7a2SEd Tanous crow::persistent_data::SessionStore::getInstance().getSessionByUid( 534b1b8683SBorawski.Lukasz params[0]); 542b7981f6SKowalski, Kamil 551abe55efSEd Tanous if (session == nullptr) 561abe55efSEd Tanous { 57f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 581abe55efSEd Tanous res.jsonValue, 591abe55efSEd Tanous messages::resourceNotFound("Session", params[0])); 60f4c4dcf4SKowalski, Kamil 61e0d918bcSEd Tanous res.result(boost::beast::http::status::not_found); 622b7981f6SKowalski, Kamil res.end(); 632b7981f6SKowalski, Kamil return; 642b7981f6SKowalski, Kamil } 652b7981f6SKowalski, Kamil 6655c7b7a2SEd Tanous Node::json["Id"] = session->uniqueId; 67c1a46bd2SBorawski.Lukasz Node::json["UserName"] = session->username; 68c1a46bd2SBorawski.Lukasz Node::json["@odata.id"] = 6955c7b7a2SEd Tanous "/redfish/v1/SessionService/Sessions/" + session->uniqueId; 702b7981f6SKowalski, Kamil 7155c7b7a2SEd Tanous res.jsonValue = Node::json; 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 85e0d918bcSEd Tanous res.result(boost::beast::http::status::bad_request); 861abe55efSEd Tanous messages::addMessageToErrorJson(res.jsonValue, 871abe55efSEd Tanous messages::generalError()); 88e0d918bcSEd Tanous 892b7981f6SKowalski, Kamil res.end(); 902b7981f6SKowalski, Kamil return; 912b7981f6SKowalski, Kamil } 922b7981f6SKowalski, Kamil 932b7981f6SKowalski, Kamil auto session = 9455c7b7a2SEd Tanous crow::persistent_data::SessionStore::getInstance().getSessionByUid( 954b1b8683SBorawski.Lukasz params[0]); 962b7981f6SKowalski, Kamil 971abe55efSEd Tanous if (session == nullptr) 981abe55efSEd Tanous { 99f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 1001abe55efSEd Tanous res.jsonValue, 1011abe55efSEd Tanous messages::resourceNotFound("Session", params[0])); 102f4c4dcf4SKowalski, Kamil 103e0d918bcSEd Tanous res.result(boost::beast::http::status::not_found); 1042b7981f6SKowalski, Kamil res.end(); 1052b7981f6SKowalski, Kamil return; 1062b7981f6SKowalski, Kamil } 1072b7981f6SKowalski, Kamil 108f4c4dcf4SKowalski, Kamil // DELETE should return representation of object that will be removed 109f4c4dcf4SKowalski, Kamil doGet(res, req, params); 110f4c4dcf4SKowalski, Kamil 1111abe55efSEd Tanous crow::persistent_data::SessionStore::getInstance().removeSession( 1121abe55efSEd Tanous session); 1132b7981f6SKowalski, Kamil } 1142b7981f6SKowalski, Kamil 1152b7981f6SKowalski, Kamil /** 1162b7981f6SKowalski, Kamil * This allows SessionCollection to reuse this class' doGet method, to 1171abe55efSEd Tanous * maintain consistency of returned data, as Collection's doPost should 1181abe55efSEd Tanous * return data for created member which should match member's doGet result 1191abe55efSEd Tanous * in 100% 1202b7981f6SKowalski, Kamil */ 1212b7981f6SKowalski, Kamil friend SessionCollection; 1222b7981f6SKowalski, Kamil }; 1232b7981f6SKowalski, Kamil 1241abe55efSEd Tanous class SessionCollection : public Node 1251abe55efSEd Tanous { 1262b7981f6SKowalski, Kamil public: 1271abe55efSEd Tanous SessionCollection(CrowApp& app) : 1281abe55efSEd Tanous Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) 1291abe55efSEd Tanous { 130c1a46bd2SBorawski.Lukasz Node::json["@odata.type"] = "#SessionCollection.SessionCollection"; 131c1a46bd2SBorawski.Lukasz Node::json["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; 132c1a46bd2SBorawski.Lukasz Node::json["@odata.context"] = 1332b7981f6SKowalski, Kamil "/redfish/v1/$metadata#SessionCollection.SessionCollection"; 134c1a46bd2SBorawski.Lukasz Node::json["Name"] = "Session Collection"; 135c1a46bd2SBorawski.Lukasz Node::json["Description"] = "Session Collection"; 136c1a46bd2SBorawski.Lukasz Node::json["Members@odata.count"] = 0; 137c1a46bd2SBorawski.Lukasz Node::json["Members"] = nlohmann::json::array(); 1383ebd75f7SEd Tanous 139e0d918bcSEd Tanous entityPrivileges = { 140e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 141e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 142e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 143e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 144e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 145e0d918bcSEd Tanous {boost::beast::http::verb::post, {}}}; 1462b7981f6SKowalski, Kamil } 1472b7981f6SKowalski, Kamil 1482b7981f6SKowalski, Kamil private: 14955c7b7a2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 1501abe55efSEd Tanous const std::vector<std::string>& params) override 1511abe55efSEd Tanous { 15255c7b7a2SEd Tanous std::vector<const std::string*> sessionIds = 15355c7b7a2SEd Tanous crow::persistent_data::SessionStore::getInstance().getUniqueIds( 15455c7b7a2SEd Tanous false, crow::persistent_data::PersistenceType::TIMEOUT); 1552b7981f6SKowalski, Kamil 15655c7b7a2SEd Tanous Node::json["Members@odata.count"] = sessionIds.size(); 157c1a46bd2SBorawski.Lukasz Node::json["Members"] = nlohmann::json::array(); 1581abe55efSEd Tanous for (const std::string* uid : sessionIds) 1591abe55efSEd Tanous { 160c1a46bd2SBorawski.Lukasz Node::json["Members"].push_back( 1612b7981f6SKowalski, Kamil {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); 1622b7981f6SKowalski, Kamil } 1632b7981f6SKowalski, Kamil 16455c7b7a2SEd Tanous res.jsonValue = Node::json; 1652b7981f6SKowalski, Kamil res.end(); 1662b7981f6SKowalski, Kamil } 1672b7981f6SKowalski, Kamil 16855c7b7a2SEd Tanous void doPost(crow::Response& res, const crow::Request& req, 1691abe55efSEd Tanous const std::vector<std::string>& params) override 1701abe55efSEd Tanous { 171*820ce598SEd Tanous nlohmann::json postRequest; 172*820ce598SEd Tanous if (!json_util::processJsonFromRequest(res, req, postRequest)) 1731abe55efSEd Tanous { 1742b7981f6SKowalski, Kamil res.end(); 1752b7981f6SKowalski, Kamil return; 1762b7981f6SKowalski, Kamil } 1772b7981f6SKowalski, Kamil 178*820ce598SEd Tanous std::string username; 179*820ce598SEd Tanous std::string password; 180*820ce598SEd Tanous for (const auto& item : postRequest.items()) 1811abe55efSEd Tanous { 182*820ce598SEd Tanous const std::string* strVal = 183*820ce598SEd Tanous item.value().get_ptr<const std::string*>(); 184*820ce598SEd Tanous if (item.key() == "UserName") 1851abe55efSEd Tanous { 186*820ce598SEd Tanous if (strVal == nullptr) 1871abe55efSEd Tanous { 188*820ce598SEd Tanous res.result(boost::beast::http::status::bad_request); 1891abe55efSEd Tanous messages::addMessageToErrorJson( 190*820ce598SEd Tanous res.jsonValue, messages::propertyValueTypeError( 191*820ce598SEd Tanous item.value().dump(), item.key())); 192*820ce598SEd Tanous continue; 193f4c4dcf4SKowalski, Kamil } 194*820ce598SEd Tanous username = *strVal; 195*820ce598SEd Tanous } 196*820ce598SEd Tanous else if (item.key() == "Password") 1971abe55efSEd Tanous { 198*820ce598SEd Tanous if (strVal == nullptr) 199*820ce598SEd Tanous { 200*820ce598SEd Tanous res.result(boost::beast::http::status::bad_request); 2011abe55efSEd Tanous messages::addMessageToErrorJson( 202*820ce598SEd Tanous res.jsonValue, messages::propertyValueTypeError( 203*820ce598SEd Tanous item.value().dump(), item.key())); 204*820ce598SEd Tanous continue; 205f4c4dcf4SKowalski, Kamil } 2062b7981f6SKowalski, Kamil 207*820ce598SEd Tanous password = *strVal; 2082b7981f6SKowalski, Kamil } 209*820ce598SEd Tanous else 2101abe55efSEd Tanous { 211*820ce598SEd Tanous res.result(boost::beast::http::status::bad_request); 212f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 213*820ce598SEd Tanous res.jsonValue, messages::propertyUnknown(item.key())); 214*820ce598SEd Tanous continue; 215*820ce598SEd Tanous } 216f4c4dcf4SKowalski, Kamil } 217f4c4dcf4SKowalski, Kamil 218*820ce598SEd Tanous if (password.empty() || username.empty() || 219*820ce598SEd Tanous res.result() != boost::beast::http::status::ok) 2201abe55efSEd Tanous { 2211abe55efSEd Tanous if (username.empty()) 2221abe55efSEd Tanous { 223*820ce598SEd Tanous res.result(boost::beast::http::status::bad_request); 2241abe55efSEd Tanous messages::addMessageToErrorJson( 225*820ce598SEd Tanous res.jsonValue, messages::propertyMissing("UserName")); 226f4c4dcf4SKowalski, Kamil } 227f4c4dcf4SKowalski, Kamil 2281abe55efSEd Tanous if (password.empty()) 2291abe55efSEd Tanous { 230*820ce598SEd Tanous res.result(boost::beast::http::status::bad_request); 2311abe55efSEd Tanous messages::addMessageToErrorJson( 232*820ce598SEd Tanous res.jsonValue, messages::propertyMissing("Password")); 233*820ce598SEd Tanous } 234*820ce598SEd Tanous res.end(); 235*820ce598SEd Tanous 236*820ce598SEd Tanous return; 237f4c4dcf4SKowalski, Kamil } 2382b7981f6SKowalski, Kamil 2391abe55efSEd Tanous if (!pamAuthenticateUser(username, password)) 2401abe55efSEd Tanous { 241f4c4dcf4SKowalski, Kamil 242*820ce598SEd Tanous res.result(boost::beast::http::status::unauthorized); 243f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 244*820ce598SEd Tanous res.jsonValue, 2451abe55efSEd Tanous messages::resourceAtUriUnauthorized( 246e0d918bcSEd Tanous std::string(req.url), "Invalid username or password")); 247*820ce598SEd Tanous res.end(); 2482b7981f6SKowalski, Kamil 249*820ce598SEd Tanous return; 2502b7981f6SKowalski, Kamil } 2512b7981f6SKowalski, Kamil 252*820ce598SEd Tanous // User is authenticated - create session 253*820ce598SEd Tanous std::shared_ptr<crow::persistent_data::UserSession> session = 254*820ce598SEd Tanous crow::persistent_data::SessionStore::getInstance() 255*820ce598SEd Tanous .generateUserSession(username); 256*820ce598SEd Tanous res.addHeader("X-Auth-Token", session->sessionToken); 257*820ce598SEd Tanous res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" + 258*820ce598SEd Tanous session->uniqueId); 259*820ce598SEd Tanous res.result(boost::beast::http::status::created); 260*820ce598SEd Tanous memberSession.doGet(res, req, {session->uniqueId}); 2612b7981f6SKowalski, Kamil } 2622b7981f6SKowalski, Kamil 2632b7981f6SKowalski, Kamil /** 2642b7981f6SKowalski, Kamil * Member session to ensure consistency between collection's doPost and 2652b7981f6SKowalski, Kamil * member's doGet, as they should return 100% matching data 2662b7981f6SKowalski, Kamil */ 2672b7981f6SKowalski, Kamil Sessions memberSession; 2682b7981f6SKowalski, Kamil }; 2692b7981f6SKowalski, Kamil 2701abe55efSEd Tanous class SessionService : public Node 2711abe55efSEd Tanous { 2725d27b854SBorawski.Lukasz public: 2731abe55efSEd Tanous SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/") 2741abe55efSEd Tanous { 2755d27b854SBorawski.Lukasz Node::json["@odata.type"] = "#SessionService.v1_0_2.SessionService"; 2765d27b854SBorawski.Lukasz Node::json["@odata.id"] = "/redfish/v1/SessionService/"; 2775d27b854SBorawski.Lukasz Node::json["@odata.context"] = 2785d27b854SBorawski.Lukasz "/redfish/v1/$metadata#SessionService.SessionService"; 2795d27b854SBorawski.Lukasz Node::json["Name"] = "Session Service"; 2806c233015SEd Tanous Node::json["Id"] = "SessionService"; 2815d27b854SBorawski.Lukasz Node::json["Description"] = "Session Service"; 2825d27b854SBorawski.Lukasz Node::json["SessionTimeout"] = 28355c7b7a2SEd Tanous crow::persistent_data::SessionStore::getInstance() 28455c7b7a2SEd Tanous .getTimeoutInSeconds(); 2855d27b854SBorawski.Lukasz Node::json["ServiceEnabled"] = true; 2863ebd75f7SEd Tanous 287e0d918bcSEd Tanous entityPrivileges = { 288e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 289e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 290e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 291e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 292e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 293e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 2945d27b854SBorawski.Lukasz } 2955d27b854SBorawski.Lukasz 2965d27b854SBorawski.Lukasz private: 29755c7b7a2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 2981abe55efSEd Tanous const std::vector<std::string>& params) override 2991abe55efSEd Tanous { 30055c7b7a2SEd Tanous res.jsonValue = Node::json; 3015d27b854SBorawski.Lukasz res.end(); 3025d27b854SBorawski.Lukasz } 3035d27b854SBorawski.Lukasz }; 3045d27b854SBorawski.Lukasz 3052b7981f6SKowalski, Kamil } // namespace redfish 306