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 222b7981f6SKowalski, Kamil namespace redfish { 232b7981f6SKowalski, Kamil 242b7981f6SKowalski, Kamil class SessionCollection; 252b7981f6SKowalski, Kamil 262b7981f6SKowalski, Kamil class Sessions : public Node { 272b7981f6SKowalski, Kamil public: 2843a095abSBorawski.Lukasz Sessions(CrowApp& app) 296c233015SEd Tanous : Node(app, "/redfish/v1/SessionService/Sessions/<str>/", std::string()) { 30c1a46bd2SBorawski.Lukasz Node::json["@odata.type"] = "#Session.v1_0_2.Session"; 31c1a46bd2SBorawski.Lukasz Node::json["@odata.context"] = "/redfish/v1/$metadata#Session.Session"; 32c1a46bd2SBorawski.Lukasz Node::json["Name"] = "User Session"; 33c1a46bd2SBorawski.Lukasz Node::json["Description"] = "Manager User Session"; 343ebd75f7SEd 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"}}}, 40e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 41e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 422b7981f6SKowalski, Kamil } 432b7981f6SKowalski, Kamil 442b7981f6SKowalski, Kamil private: 4555c7b7a2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 462b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 472b7981f6SKowalski, Kamil auto session = 4855c7b7a2SEd Tanous crow::persistent_data::SessionStore::getInstance().getSessionByUid( 494b1b8683SBorawski.Lukasz params[0]); 502b7981f6SKowalski, Kamil 512b7981f6SKowalski, Kamil if (session == nullptr) { 52f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 5355c7b7a2SEd Tanous res.jsonValue, messages::resourceNotFound("Session", params[0])); 54f4c4dcf4SKowalski, Kamil 55e0d918bcSEd Tanous res.result(boost::beast::http::status::not_found); 562b7981f6SKowalski, Kamil res.end(); 572b7981f6SKowalski, Kamil return; 582b7981f6SKowalski, Kamil } 592b7981f6SKowalski, Kamil 6055c7b7a2SEd Tanous Node::json["Id"] = session->uniqueId; 61c1a46bd2SBorawski.Lukasz Node::json["UserName"] = session->username; 62c1a46bd2SBorawski.Lukasz Node::json["@odata.id"] = 6355c7b7a2SEd Tanous "/redfish/v1/SessionService/Sessions/" + session->uniqueId; 642b7981f6SKowalski, Kamil 6555c7b7a2SEd Tanous res.jsonValue = Node::json; 662b7981f6SKowalski, Kamil res.end(); 672b7981f6SKowalski, Kamil } 682b7981f6SKowalski, Kamil 6955c7b7a2SEd Tanous void doDelete(crow::Response& res, const crow::Request& req, 702b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 712b7981f6SKowalski, Kamil // Need only 1 param which should be id of session to be deleted 722b7981f6SKowalski, Kamil if (params.size() != 1) { 73f4c4dcf4SKowalski, Kamil // This should be handled by crow and never happen 7455c7b7a2SEd Tanous BMCWEB_LOG_ERROR 75f4c4dcf4SKowalski, Kamil << "Session DELETE has been called with invalid number of params"; 76f4c4dcf4SKowalski, Kamil 77e0d918bcSEd Tanous res.result(boost::beast::http::status::bad_request); 7855c7b7a2SEd Tanous messages::addMessageToErrorJson(res.jsonValue, messages::generalError()); 79e0d918bcSEd Tanous 802b7981f6SKowalski, Kamil res.end(); 812b7981f6SKowalski, Kamil return; 822b7981f6SKowalski, Kamil } 832b7981f6SKowalski, Kamil 842b7981f6SKowalski, Kamil auto session = 8555c7b7a2SEd Tanous crow::persistent_data::SessionStore::getInstance().getSessionByUid( 864b1b8683SBorawski.Lukasz params[0]); 872b7981f6SKowalski, Kamil 882b7981f6SKowalski, Kamil if (session == nullptr) { 89f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 9055c7b7a2SEd Tanous res.jsonValue, messages::resourceNotFound("Session", params[0])); 91f4c4dcf4SKowalski, Kamil 92e0d918bcSEd Tanous res.result(boost::beast::http::status::not_found); 932b7981f6SKowalski, Kamil res.end(); 942b7981f6SKowalski, Kamil return; 952b7981f6SKowalski, Kamil } 962b7981f6SKowalski, Kamil 97f4c4dcf4SKowalski, Kamil // DELETE should return representation of object that will be removed 98f4c4dcf4SKowalski, Kamil doGet(res, req, params); 99f4c4dcf4SKowalski, Kamil 10055c7b7a2SEd Tanous crow::persistent_data::SessionStore::getInstance().removeSession(session); 1012b7981f6SKowalski, Kamil } 1022b7981f6SKowalski, Kamil 1032b7981f6SKowalski, Kamil /** 1042b7981f6SKowalski, Kamil * This allows SessionCollection to reuse this class' doGet method, to 1052b7981f6SKowalski, Kamil * maintain consistency of returned data, as Collection's doPost should return 1062b7981f6SKowalski, Kamil * data for created member which should match member's doGet result in 100% 1072b7981f6SKowalski, Kamil */ 1082b7981f6SKowalski, Kamil friend SessionCollection; 1092b7981f6SKowalski, Kamil }; 1102b7981f6SKowalski, Kamil 1112b7981f6SKowalski, Kamil class SessionCollection : public Node { 1122b7981f6SKowalski, Kamil public: 11343a095abSBorawski.Lukasz SessionCollection(CrowApp& app) 1143ebd75f7SEd Tanous : Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) { 115c1a46bd2SBorawski.Lukasz Node::json["@odata.type"] = "#SessionCollection.SessionCollection"; 116c1a46bd2SBorawski.Lukasz Node::json["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; 117c1a46bd2SBorawski.Lukasz Node::json["@odata.context"] = 1182b7981f6SKowalski, Kamil "/redfish/v1/$metadata#SessionCollection.SessionCollection"; 119c1a46bd2SBorawski.Lukasz Node::json["Name"] = "Session Collection"; 120c1a46bd2SBorawski.Lukasz Node::json["Description"] = "Session Collection"; 121c1a46bd2SBorawski.Lukasz Node::json["Members@odata.count"] = 0; 122c1a46bd2SBorawski.Lukasz Node::json["Members"] = nlohmann::json::array(); 1233ebd75f7SEd Tanous 124e0d918bcSEd Tanous entityPrivileges = { 125e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 126e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 127e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 128e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 129e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 130e0d918bcSEd Tanous {boost::beast::http::verb::post, {}}}; 1312b7981f6SKowalski, Kamil } 1322b7981f6SKowalski, Kamil 1332b7981f6SKowalski, Kamil private: 13455c7b7a2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 1352b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 13655c7b7a2SEd Tanous std::vector<const std::string*> sessionIds = 13755c7b7a2SEd Tanous crow::persistent_data::SessionStore::getInstance().getUniqueIds( 13855c7b7a2SEd Tanous false, crow::persistent_data::PersistenceType::TIMEOUT); 1392b7981f6SKowalski, Kamil 14055c7b7a2SEd Tanous Node::json["Members@odata.count"] = sessionIds.size(); 141c1a46bd2SBorawski.Lukasz Node::json["Members"] = nlohmann::json::array(); 14255c7b7a2SEd Tanous for (const std::string* uid : sessionIds) { 143c1a46bd2SBorawski.Lukasz Node::json["Members"].push_back( 1442b7981f6SKowalski, Kamil {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); 1452b7981f6SKowalski, Kamil } 1462b7981f6SKowalski, Kamil 14755c7b7a2SEd Tanous res.jsonValue = Node::json; 1482b7981f6SKowalski, Kamil res.end(); 1492b7981f6SKowalski, Kamil } 1502b7981f6SKowalski, Kamil 15155c7b7a2SEd Tanous void doPost(crow::Response& res, const crow::Request& req, 1522b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 153e0d918bcSEd Tanous boost::beast::http::status status; 1542b7981f6SKowalski, Kamil std::string username; 155f4c4dcf4SKowalski, Kamil bool userAuthSuccessful = 15655c7b7a2SEd Tanous authenticateUser(req, status, username, res.jsonValue); 157e0d918bcSEd Tanous res.result(status); 158e0d918bcSEd Tanous 1592b7981f6SKowalski, Kamil if (!userAuthSuccessful) { 1602b7981f6SKowalski, Kamil res.end(); 1612b7981f6SKowalski, Kamil return; 1622b7981f6SKowalski, Kamil } 1632b7981f6SKowalski, Kamil 1642b7981f6SKowalski, Kamil // User is authenticated - create session for him 1652b7981f6SKowalski, Kamil auto session = 16655c7b7a2SEd Tanous crow::persistent_data::SessionStore::getInstance().generateUserSession( 1674b1b8683SBorawski.Lukasz username); 16855c7b7a2SEd Tanous res.addHeader("X-Auth-Token", session->sessionToken); 1692b7981f6SKowalski, Kamil 170*b9845d9eSEd Tanous res.addHeader("Location", 171*b9845d9eSEd Tanous "/redfish/v1/SessionService/Sessions/" + session->uniqueId); 172*b9845d9eSEd Tanous 1732b7981f6SKowalski, Kamil // Return data for created session 17455c7b7a2SEd Tanous memberSession.doGet(res, req, {session->uniqueId}); 1752b7981f6SKowalski, Kamil 1762b7981f6SKowalski, Kamil // No need for res.end(), as it is called by doGet() 1772b7981f6SKowalski, Kamil } 1782b7981f6SKowalski, Kamil 1792b7981f6SKowalski, Kamil /** 1802b7981f6SKowalski, Kamil * @brief Verifies data provided in request and tries to authenticate user 1812b7981f6SKowalski, Kamil * 1822b7981f6SKowalski, Kamil * @param[in] req Crow request containing authentication data 1832b7981f6SKowalski, Kamil * @param[out] httpRespCode HTTP Code that should be returned in response 1842b7981f6SKowalski, Kamil * @param[out] user Retrieved username - not filled on failure 185f4c4dcf4SKowalski, Kamil * @param[out] errJson JSON to which error messages will be written 1862b7981f6SKowalski, Kamil * 1872b7981f6SKowalski, Kamil * @return true if authentication was successful, false otherwise 1882b7981f6SKowalski, Kamil */ 18955c7b7a2SEd Tanous bool authenticateUser(const crow::Request& req, 190e0d918bcSEd Tanous boost::beast::http::status& httpRespCode, 191f4c4dcf4SKowalski, Kamil std::string& user, nlohmann::json& errJson) { 1922b7981f6SKowalski, Kamil // We need only UserName and Password - nothing more, nothing less 1932b7981f6SKowalski, Kamil static constexpr const unsigned int numberOfRequiredFieldsInReq = 2; 1942b7981f6SKowalski, Kamil 1952b7981f6SKowalski, Kamil // call with exceptions disabled 19655c7b7a2SEd Tanous auto loginCredentials = nlohmann::json::parse(req.body, nullptr, false); 19755c7b7a2SEd Tanous if (loginCredentials.is_discarded()) { 198e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 199f4c4dcf4SKowalski, Kamil 200f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, messages::malformedJSON()); 2012b7981f6SKowalski, Kamil 2022b7981f6SKowalski, Kamil return false; 2032b7981f6SKowalski, Kamil } 2042b7981f6SKowalski, Kamil 2052b7981f6SKowalski, Kamil // Check that there are only as many fields as there should be 20655c7b7a2SEd Tanous if (loginCredentials.size() != numberOfRequiredFieldsInReq) { 207e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 208f4c4dcf4SKowalski, Kamil 209f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, messages::malformedJSON()); 2102b7981f6SKowalski, Kamil 2112b7981f6SKowalski, Kamil return false; 2122b7981f6SKowalski, Kamil } 2132b7981f6SKowalski, Kamil 2142b7981f6SKowalski, Kamil // Find fields that we need - UserName and Password 21555c7b7a2SEd Tanous auto userIt = loginCredentials.find("UserName"); 21655c7b7a2SEd Tanous auto passIt = loginCredentials.find("Password"); 21755c7b7a2SEd Tanous if (userIt == loginCredentials.end() || passIt == loginCredentials.end()) { 218e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 219f4c4dcf4SKowalski, Kamil 22055c7b7a2SEd Tanous if (userIt == loginCredentials.end()) { 221f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, 222f4c4dcf4SKowalski, Kamil messages::propertyMissing("UserName")); 223f4c4dcf4SKowalski, Kamil } 224f4c4dcf4SKowalski, Kamil 22555c7b7a2SEd Tanous if (passIt == loginCredentials.end()) { 226f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, 227f4c4dcf4SKowalski, Kamil messages::propertyMissing("Password")); 228f4c4dcf4SKowalski, Kamil } 2292b7981f6SKowalski, Kamil 2302b7981f6SKowalski, Kamil return false; 2312b7981f6SKowalski, Kamil } 2322b7981f6SKowalski, Kamil 2332b7981f6SKowalski, Kamil // Check that given data is of valid type (string) 23455c7b7a2SEd Tanous if (!userIt->is_string() || !passIt->is_string()) { 235e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 236f4c4dcf4SKowalski, Kamil 23755c7b7a2SEd Tanous if (!userIt->is_string()) { 238f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 239f4c4dcf4SKowalski, Kamil errJson, 24055c7b7a2SEd Tanous messages::propertyValueTypeError(userIt->dump(), "UserName")); 241f4c4dcf4SKowalski, Kamil } 242f4c4dcf4SKowalski, Kamil 24355c7b7a2SEd Tanous if (!passIt->is_string()) { 244f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 245f4c4dcf4SKowalski, Kamil errJson, 24655c7b7a2SEd Tanous messages::propertyValueTypeError(userIt->dump(), "Password")); 247f4c4dcf4SKowalski, Kamil } 2482b7981f6SKowalski, Kamil 2492b7981f6SKowalski, Kamil return false; 2502b7981f6SKowalski, Kamil } 2512b7981f6SKowalski, Kamil 2522b7981f6SKowalski, Kamil // Extract username and password 25355c7b7a2SEd Tanous std::string username = userIt->get<const std::string>(); 25455c7b7a2SEd Tanous std::string password = passIt->get<const std::string>(); 2552b7981f6SKowalski, Kamil 2562b7981f6SKowalski, Kamil // Verify that required fields are not empty 2572b7981f6SKowalski, Kamil if (username.empty() || password.empty()) { 258e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 259f4c4dcf4SKowalski, Kamil 260f4c4dcf4SKowalski, Kamil if (username.empty()) { 261f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, 262f4c4dcf4SKowalski, Kamil messages::propertyMissing("UserName")); 263f4c4dcf4SKowalski, Kamil } 264f4c4dcf4SKowalski, Kamil 265f4c4dcf4SKowalski, Kamil if (password.empty()) { 266f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, 267f4c4dcf4SKowalski, Kamil messages::propertyMissing("Password")); 268f4c4dcf4SKowalski, Kamil } 2692b7981f6SKowalski, Kamil 2702b7981f6SKowalski, Kamil return false; 2712b7981f6SKowalski, Kamil } 2722b7981f6SKowalski, Kamil 2732b7981f6SKowalski, Kamil // Finally - try to authenticate user 27455c7b7a2SEd Tanous if (!pamAuthenticateUser(username, password)) { 275e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::unauthorized; 276f4c4dcf4SKowalski, Kamil 277f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 278f4c4dcf4SKowalski, Kamil errJson, messages::resourceAtUriUnauthorized( 279e0d918bcSEd Tanous std::string(req.url), "Invalid username or password")); 2802b7981f6SKowalski, Kamil 2812b7981f6SKowalski, Kamil return false; 2822b7981f6SKowalski, Kamil } 2832b7981f6SKowalski, Kamil 2842b7981f6SKowalski, Kamil // User authenticated successfully 285e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::ok; 286f4c4dcf4SKowalski, Kamil user = username; 2872b7981f6SKowalski, Kamil 2882b7981f6SKowalski, Kamil return true; 2892b7981f6SKowalski, Kamil } 2902b7981f6SKowalski, Kamil 2912b7981f6SKowalski, Kamil /** 2922b7981f6SKowalski, Kamil * Member session to ensure consistency between collection's doPost and 2932b7981f6SKowalski, Kamil * member's doGet, as they should return 100% matching data 2942b7981f6SKowalski, Kamil */ 2952b7981f6SKowalski, Kamil Sessions memberSession; 2962b7981f6SKowalski, Kamil }; 2972b7981f6SKowalski, Kamil 2985d27b854SBorawski.Lukasz class SessionService : public Node { 2995d27b854SBorawski.Lukasz public: 3003ebd75f7SEd Tanous SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/") { 3015d27b854SBorawski.Lukasz Node::json["@odata.type"] = "#SessionService.v1_0_2.SessionService"; 3025d27b854SBorawski.Lukasz Node::json["@odata.id"] = "/redfish/v1/SessionService/"; 3035d27b854SBorawski.Lukasz Node::json["@odata.context"] = 3045d27b854SBorawski.Lukasz "/redfish/v1/$metadata#SessionService.SessionService"; 3055d27b854SBorawski.Lukasz Node::json["Name"] = "Session Service"; 3066c233015SEd Tanous Node::json["Id"] = "SessionService"; 3075d27b854SBorawski.Lukasz Node::json["Description"] = "Session Service"; 3085d27b854SBorawski.Lukasz Node::json["SessionTimeout"] = 30955c7b7a2SEd Tanous crow::persistent_data::SessionStore::getInstance() 31055c7b7a2SEd Tanous .getTimeoutInSeconds(); 3115d27b854SBorawski.Lukasz Node::json["ServiceEnabled"] = true; 3123ebd75f7SEd Tanous 313e0d918bcSEd Tanous entityPrivileges = { 314e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 315e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 316e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 317e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 318e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 319e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 3205d27b854SBorawski.Lukasz } 3215d27b854SBorawski.Lukasz 3225d27b854SBorawski.Lukasz private: 32355c7b7a2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 3245d27b854SBorawski.Lukasz const std::vector<std::string>& params) override { 32555c7b7a2SEd Tanous res.jsonValue = Node::json; 3265d27b854SBorawski.Lukasz res.end(); 3275d27b854SBorawski.Lukasz } 3285d27b854SBorawski.Lukasz }; 3295d27b854SBorawski.Lukasz 3302b7981f6SKowalski, Kamil } // namespace redfish 331