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" 202b7981f6SKowalski, Kamil #include "session_storage_singleton.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 35*e0d918bcSEd Tanous entityPrivileges = { 36*e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 37*e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 38*e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 39*e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 40*e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 41*e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 422b7981f6SKowalski, Kamil } 432b7981f6SKowalski, Kamil 442b7981f6SKowalski, Kamil private: 452b7981f6SKowalski, Kamil void doGet(crow::response& res, const crow::request& req, 462b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 472b7981f6SKowalski, Kamil auto session = 482b7981f6SKowalski, Kamil crow::PersistentData::session_store->get_session_by_uid(params[0]); 492b7981f6SKowalski, Kamil 502b7981f6SKowalski, Kamil if (session == nullptr) { 51f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 52f4c4dcf4SKowalski, Kamil res.json_value, messages::resourceNotFound("Session", params[0])); 53f4c4dcf4SKowalski, Kamil 54*e0d918bcSEd Tanous res.result(boost::beast::http::status::not_found); 552b7981f6SKowalski, Kamil res.end(); 562b7981f6SKowalski, Kamil return; 572b7981f6SKowalski, Kamil } 582b7981f6SKowalski, Kamil 59c1a46bd2SBorawski.Lukasz Node::json["Id"] = session->unique_id; 60c1a46bd2SBorawski.Lukasz Node::json["UserName"] = session->username; 61c1a46bd2SBorawski.Lukasz Node::json["@odata.id"] = 622b7981f6SKowalski, Kamil "/redfish/v1/SessionService/Sessions/" + session->unique_id; 632b7981f6SKowalski, Kamil 64c1a46bd2SBorawski.Lukasz res.json_value = Node::json; 652b7981f6SKowalski, Kamil res.end(); 662b7981f6SKowalski, Kamil } 672b7981f6SKowalski, Kamil 682b7981f6SKowalski, Kamil void doDelete(crow::response& res, const crow::request& req, 692b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 702b7981f6SKowalski, Kamil // Need only 1 param which should be id of session to be deleted 712b7981f6SKowalski, Kamil if (params.size() != 1) { 72f4c4dcf4SKowalski, Kamil // This should be handled by crow and never happen 73f4c4dcf4SKowalski, Kamil CROW_LOG_ERROR 74f4c4dcf4SKowalski, Kamil << "Session DELETE has been called with invalid number of params"; 75f4c4dcf4SKowalski, Kamil 76*e0d918bcSEd Tanous res.result(boost::beast::http::status::bad_request); 77f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(res.json_value, messages::generalError()); 78*e0d918bcSEd Tanous 792b7981f6SKowalski, Kamil res.end(); 802b7981f6SKowalski, Kamil return; 812b7981f6SKowalski, Kamil } 822b7981f6SKowalski, Kamil 832b7981f6SKowalski, Kamil auto session = 842b7981f6SKowalski, Kamil crow::PersistentData::session_store->get_session_by_uid(params[0]); 852b7981f6SKowalski, Kamil 862b7981f6SKowalski, Kamil if (session == nullptr) { 87f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 88f4c4dcf4SKowalski, Kamil res.json_value, messages::resourceNotFound("Session", params[0])); 89f4c4dcf4SKowalski, Kamil 90*e0d918bcSEd Tanous res.result(boost::beast::http::status::not_found); 912b7981f6SKowalski, Kamil res.end(); 922b7981f6SKowalski, Kamil return; 932b7981f6SKowalski, Kamil } 942b7981f6SKowalski, Kamil 95f4c4dcf4SKowalski, Kamil // DELETE should return representation of object that will be removed 96f4c4dcf4SKowalski, Kamil doGet(res, req, params); 97f4c4dcf4SKowalski, Kamil 982b7981f6SKowalski, Kamil crow::PersistentData::session_store->remove_session(session); 992b7981f6SKowalski, Kamil } 1002b7981f6SKowalski, Kamil 1012b7981f6SKowalski, Kamil /** 1022b7981f6SKowalski, Kamil * This allows SessionCollection to reuse this class' doGet method, to 1032b7981f6SKowalski, Kamil * maintain consistency of returned data, as Collection's doPost should return 1042b7981f6SKowalski, Kamil * data for created member which should match member's doGet result in 100% 1052b7981f6SKowalski, Kamil */ 1062b7981f6SKowalski, Kamil friend SessionCollection; 1072b7981f6SKowalski, Kamil }; 1082b7981f6SKowalski, Kamil 1092b7981f6SKowalski, Kamil class SessionCollection : public Node { 1102b7981f6SKowalski, Kamil public: 11143a095abSBorawski.Lukasz SessionCollection(CrowApp& app) 1123ebd75f7SEd Tanous : Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) { 113c1a46bd2SBorawski.Lukasz Node::json["@odata.type"] = "#SessionCollection.SessionCollection"; 114c1a46bd2SBorawski.Lukasz Node::json["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; 115c1a46bd2SBorawski.Lukasz Node::json["@odata.context"] = 1162b7981f6SKowalski, Kamil "/redfish/v1/$metadata#SessionCollection.SessionCollection"; 117c1a46bd2SBorawski.Lukasz Node::json["Name"] = "Session Collection"; 118c1a46bd2SBorawski.Lukasz Node::json["Description"] = "Session Collection"; 119c1a46bd2SBorawski.Lukasz Node::json["Members@odata.count"] = 0; 120c1a46bd2SBorawski.Lukasz Node::json["Members"] = nlohmann::json::array(); 1213ebd75f7SEd Tanous 122*e0d918bcSEd Tanous entityPrivileges = { 123*e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 124*e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 125*e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 126*e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 127*e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 128*e0d918bcSEd Tanous {boost::beast::http::verb::post, {}}}; 1292b7981f6SKowalski, Kamil } 1302b7981f6SKowalski, Kamil 1312b7981f6SKowalski, Kamil private: 1322b7981f6SKowalski, Kamil void doGet(crow::response& res, const crow::request& req, 1332b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 1342b7981f6SKowalski, Kamil std::vector<const std::string*> session_ids = 1352b7981f6SKowalski, Kamil crow::PersistentData::session_store->get_unique_ids( 1362b7981f6SKowalski, Kamil false, crow::PersistentData::PersistenceType::TIMEOUT); 1372b7981f6SKowalski, Kamil 138c1a46bd2SBorawski.Lukasz Node::json["Members@odata.count"] = session_ids.size(); 139c1a46bd2SBorawski.Lukasz Node::json["Members"] = nlohmann::json::array(); 140*e0d918bcSEd Tanous for (const std::string* uid : session_ids) { 141c1a46bd2SBorawski.Lukasz Node::json["Members"].push_back( 1422b7981f6SKowalski, Kamil {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); 1432b7981f6SKowalski, Kamil } 1442b7981f6SKowalski, Kamil 145c1a46bd2SBorawski.Lukasz res.json_value = Node::json; 1462b7981f6SKowalski, Kamil res.end(); 1472b7981f6SKowalski, Kamil } 1482b7981f6SKowalski, Kamil 1492b7981f6SKowalski, Kamil void doPost(crow::response& res, const crow::request& req, 1502b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 151*e0d918bcSEd Tanous boost::beast::http::status status; 1522b7981f6SKowalski, Kamil std::string username; 153f4c4dcf4SKowalski, Kamil bool userAuthSuccessful = 154*e0d918bcSEd Tanous authenticateUser(req, status, username, res.json_value); 155*e0d918bcSEd Tanous res.result(status); 156*e0d918bcSEd Tanous 1572b7981f6SKowalski, Kamil if (!userAuthSuccessful) { 1582b7981f6SKowalski, Kamil res.end(); 1592b7981f6SKowalski, Kamil return; 1602b7981f6SKowalski, Kamil } 1612b7981f6SKowalski, Kamil 1622b7981f6SKowalski, Kamil // User is authenticated - create session for him 1632b7981f6SKowalski, Kamil auto session = 1642b7981f6SKowalski, Kamil crow::PersistentData::session_store->generate_user_session(username); 165*e0d918bcSEd Tanous res.add_header("X-Auth-Token", session->session_token); 1662b7981f6SKowalski, Kamil 1672b7981f6SKowalski, Kamil // Return data for created session 168*e0d918bcSEd Tanous memberSession.doGet(res, req, {session->unique_id}); 1692b7981f6SKowalski, Kamil 1702b7981f6SKowalski, Kamil // No need for res.end(), as it is called by doGet() 1712b7981f6SKowalski, Kamil } 1722b7981f6SKowalski, Kamil 1732b7981f6SKowalski, Kamil /** 1742b7981f6SKowalski, Kamil * @brief Verifies data provided in request and tries to authenticate user 1752b7981f6SKowalski, Kamil * 1762b7981f6SKowalski, Kamil * @param[in] req Crow request containing authentication data 1772b7981f6SKowalski, Kamil * @param[out] httpRespCode HTTP Code that should be returned in response 1782b7981f6SKowalski, Kamil * @param[out] user Retrieved username - not filled on failure 179f4c4dcf4SKowalski, Kamil * @param[out] errJson JSON to which error messages will be written 1802b7981f6SKowalski, Kamil * 1812b7981f6SKowalski, Kamil * @return true if authentication was successful, false otherwise 1822b7981f6SKowalski, Kamil */ 183*e0d918bcSEd Tanous bool authenticateUser(const crow::request& req, 184*e0d918bcSEd Tanous boost::beast::http::status& httpRespCode, 185f4c4dcf4SKowalski, Kamil std::string& user, nlohmann::json& errJson) { 1862b7981f6SKowalski, Kamil // We need only UserName and Password - nothing more, nothing less 1872b7981f6SKowalski, Kamil static constexpr const unsigned int numberOfRequiredFieldsInReq = 2; 1882b7981f6SKowalski, Kamil 1892b7981f6SKowalski, Kamil // call with exceptions disabled 1902b7981f6SKowalski, Kamil auto login_credentials = nlohmann::json::parse(req.body, nullptr, false); 1912b7981f6SKowalski, Kamil if (login_credentials.is_discarded()) { 192*e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 193f4c4dcf4SKowalski, Kamil 194f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, messages::malformedJSON()); 1952b7981f6SKowalski, Kamil 1962b7981f6SKowalski, Kamil return false; 1972b7981f6SKowalski, Kamil } 1982b7981f6SKowalski, Kamil 1992b7981f6SKowalski, Kamil // Check that there are only as many fields as there should be 2002b7981f6SKowalski, Kamil if (login_credentials.size() != numberOfRequiredFieldsInReq) { 201*e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 202f4c4dcf4SKowalski, Kamil 203f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, messages::malformedJSON()); 2042b7981f6SKowalski, Kamil 2052b7981f6SKowalski, Kamil return false; 2062b7981f6SKowalski, Kamil } 2072b7981f6SKowalski, Kamil 2082b7981f6SKowalski, Kamil // Find fields that we need - UserName and Password 2092b7981f6SKowalski, Kamil auto user_it = login_credentials.find("UserName"); 2102b7981f6SKowalski, Kamil auto pass_it = login_credentials.find("Password"); 2112b7981f6SKowalski, Kamil if (user_it == login_credentials.end() || 2122b7981f6SKowalski, Kamil pass_it == login_credentials.end()) { 213*e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 214f4c4dcf4SKowalski, Kamil 215f4c4dcf4SKowalski, Kamil if (user_it == login_credentials.end()) { 216f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, 217f4c4dcf4SKowalski, Kamil messages::propertyMissing("UserName")); 218f4c4dcf4SKowalski, Kamil } 219f4c4dcf4SKowalski, Kamil 220f4c4dcf4SKowalski, Kamil if (pass_it == login_credentials.end()) { 221f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, 222f4c4dcf4SKowalski, Kamil messages::propertyMissing("Password")); 223f4c4dcf4SKowalski, Kamil } 2242b7981f6SKowalski, Kamil 2252b7981f6SKowalski, Kamil return false; 2262b7981f6SKowalski, Kamil } 2272b7981f6SKowalski, Kamil 2282b7981f6SKowalski, Kamil // Check that given data is of valid type (string) 2292b7981f6SKowalski, Kamil if (!user_it->is_string() || !pass_it->is_string()) { 230*e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 231f4c4dcf4SKowalski, Kamil 232f4c4dcf4SKowalski, Kamil if (!user_it->is_string()) { 233f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 234f4c4dcf4SKowalski, Kamil errJson, 235f4c4dcf4SKowalski, Kamil messages::propertyValueTypeError(user_it->dump(), "UserName")); 236f4c4dcf4SKowalski, Kamil } 237f4c4dcf4SKowalski, Kamil 238f4c4dcf4SKowalski, Kamil if (!pass_it->is_string()) { 239f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 240f4c4dcf4SKowalski, Kamil errJson, 241f4c4dcf4SKowalski, Kamil messages::propertyValueTypeError(user_it->dump(), "Password")); 242f4c4dcf4SKowalski, Kamil } 2432b7981f6SKowalski, Kamil 2442b7981f6SKowalski, Kamil return false; 2452b7981f6SKowalski, Kamil } 2462b7981f6SKowalski, Kamil 2472b7981f6SKowalski, Kamil // Extract username and password 2482b7981f6SKowalski, Kamil std::string username = user_it->get<const std::string>(); 2492b7981f6SKowalski, Kamil std::string password = pass_it->get<const std::string>(); 2502b7981f6SKowalski, Kamil 2512b7981f6SKowalski, Kamil // Verify that required fields are not empty 2522b7981f6SKowalski, Kamil if (username.empty() || password.empty()) { 253*e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 254f4c4dcf4SKowalski, Kamil 255f4c4dcf4SKowalski, Kamil if (username.empty()) { 256f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, 257f4c4dcf4SKowalski, Kamil messages::propertyMissing("UserName")); 258f4c4dcf4SKowalski, Kamil } 259f4c4dcf4SKowalski, Kamil 260f4c4dcf4SKowalski, Kamil if (password.empty()) { 261f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, 262f4c4dcf4SKowalski, Kamil messages::propertyMissing("Password")); 263f4c4dcf4SKowalski, Kamil } 2642b7981f6SKowalski, Kamil 2652b7981f6SKowalski, Kamil return false; 2662b7981f6SKowalski, Kamil } 2672b7981f6SKowalski, Kamil 2682b7981f6SKowalski, Kamil // Finally - try to authenticate user 2692b7981f6SKowalski, Kamil if (!pam_authenticate_user(username, password)) { 270*e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::unauthorized; 271f4c4dcf4SKowalski, Kamil 272f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 273f4c4dcf4SKowalski, Kamil errJson, messages::resourceAtUriUnauthorized( 274*e0d918bcSEd Tanous std::string(req.url), "Invalid username or password")); 2752b7981f6SKowalski, Kamil 2762b7981f6SKowalski, Kamil return false; 2772b7981f6SKowalski, Kamil } 2782b7981f6SKowalski, Kamil 2792b7981f6SKowalski, Kamil // User authenticated successfully 280*e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::ok; 281f4c4dcf4SKowalski, Kamil user = username; 2822b7981f6SKowalski, Kamil 2832b7981f6SKowalski, Kamil return true; 2842b7981f6SKowalski, Kamil } 2852b7981f6SKowalski, Kamil 2862b7981f6SKowalski, Kamil /** 2872b7981f6SKowalski, Kamil * Member session to ensure consistency between collection's doPost and 2882b7981f6SKowalski, Kamil * member's doGet, as they should return 100% matching data 2892b7981f6SKowalski, Kamil */ 2902b7981f6SKowalski, Kamil Sessions memberSession; 2912b7981f6SKowalski, Kamil }; 2922b7981f6SKowalski, Kamil 2935d27b854SBorawski.Lukasz class SessionService : public Node { 2945d27b854SBorawski.Lukasz public: 2953ebd75f7SEd Tanous SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/") { 2965d27b854SBorawski.Lukasz Node::json["@odata.type"] = "#SessionService.v1_0_2.SessionService"; 2975d27b854SBorawski.Lukasz Node::json["@odata.id"] = "/redfish/v1/SessionService/"; 2985d27b854SBorawski.Lukasz Node::json["@odata.context"] = 2995d27b854SBorawski.Lukasz "/redfish/v1/$metadata#SessionService.SessionService"; 3005d27b854SBorawski.Lukasz Node::json["Name"] = "Session Service"; 3016c233015SEd Tanous Node::json["Id"] = "SessionService"; 3025d27b854SBorawski.Lukasz Node::json["Description"] = "Session Service"; 3035d27b854SBorawski.Lukasz Node::json["SessionTimeout"] = 3045d27b854SBorawski.Lukasz crow::PersistentData::session_store->get_timeout_in_seconds(); 3055d27b854SBorawski.Lukasz Node::json["ServiceEnabled"] = true; 3063ebd75f7SEd Tanous 307*e0d918bcSEd Tanous entityPrivileges = { 308*e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 309*e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 310*e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 311*e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 312*e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 313*e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 3145d27b854SBorawski.Lukasz } 3155d27b854SBorawski.Lukasz 3165d27b854SBorawski.Lukasz private: 3175d27b854SBorawski.Lukasz void doGet(crow::response& res, const crow::request& req, 3185d27b854SBorawski.Lukasz const std::vector<std::string>& params) override { 3195d27b854SBorawski.Lukasz res.json_value = Node::json; 3205d27b854SBorawski.Lukasz res.end(); 3215d27b854SBorawski.Lukasz } 3225d27b854SBorawski.Lukasz }; 3235d27b854SBorawski.Lukasz 3242b7981f6SKowalski, Kamil } // namespace redfish 325