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 17*43a095abSBorawski.Lukasz 182b7981f6SKowalski, Kamil #include "node.hpp" 192b7981f6SKowalski, Kamil #include "session_storage_singleton.hpp" 202b7981f6SKowalski, Kamil 212b7981f6SKowalski, Kamil namespace redfish { 222b7981f6SKowalski, Kamil 23*43a095abSBorawski.Lukasz static OperationMap sessionOpMap = { 24*43a095abSBorawski.Lukasz {crow::HTTPMethod::GET, {{"Login"}}}, 25*43a095abSBorawski.Lukasz {crow::HTTPMethod::HEAD, {{"Login"}}}, 26*43a095abSBorawski.Lukasz {crow::HTTPMethod::PATCH, {{"ConfigureManager"}}}, 27*43a095abSBorawski.Lukasz {crow::HTTPMethod::PUT, {{"ConfigureManager"}}}, 28*43a095abSBorawski.Lukasz {crow::HTTPMethod::DELETE, {{"ConfigureManager"}}}, 29*43a095abSBorawski.Lukasz {crow::HTTPMethod::POST, {{"ConfigureManager"}}}}; 30*43a095abSBorawski.Lukasz 31*43a095abSBorawski.Lukasz static OperationMap sessionCollectionOpMap = { 32*43a095abSBorawski.Lukasz {crow::HTTPMethod::GET, {{"Login"}}}, 33*43a095abSBorawski.Lukasz {crow::HTTPMethod::HEAD, {{"Login"}}}, 34*43a095abSBorawski.Lukasz {crow::HTTPMethod::PATCH, {{"ConfigureManager"}}}, 35*43a095abSBorawski.Lukasz {crow::HTTPMethod::PUT, {{"ConfigureManager"}}}, 36*43a095abSBorawski.Lukasz {crow::HTTPMethod::DELETE, {{"ConfigureManager"}}}, 37*43a095abSBorawski.Lukasz {crow::HTTPMethod::POST, {{}}}}; 38*43a095abSBorawski.Lukasz 392b7981f6SKowalski, Kamil class SessionCollection; 402b7981f6SKowalski, Kamil 412b7981f6SKowalski, Kamil class Sessions : public Node { 422b7981f6SKowalski, Kamil public: 43*43a095abSBorawski.Lukasz template <typename CrowApp> 44*43a095abSBorawski.Lukasz Sessions(CrowApp& app) 45*43a095abSBorawski.Lukasz : Node(app, EntityPrivileges(std::move(sessionOpMap)), 462b7981f6SKowalski, Kamil "/redfish/v1/SessionService/Sessions/<str>", std::string()) { 47aecb47a4SBorawski.Lukasz nodeJson["@odata.type"] = "#Session.v1_0_2.Session"; 482b7981f6SKowalski, Kamil nodeJson["@odata.context"] = "/redfish/v1/$metadata#Session.Session"; 492b7981f6SKowalski, Kamil nodeJson["Name"] = "User Session"; 502b7981f6SKowalski, Kamil nodeJson["Description"] = "Manager User Session"; 512b7981f6SKowalski, Kamil } 522b7981f6SKowalski, Kamil 532b7981f6SKowalski, Kamil private: 542b7981f6SKowalski, Kamil void doGet(crow::response& res, const crow::request& req, 552b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 562b7981f6SKowalski, Kamil auto session = 572b7981f6SKowalski, Kamil crow::PersistentData::session_store->get_session_by_uid(params[0]); 582b7981f6SKowalski, Kamil 592b7981f6SKowalski, Kamil if (session == nullptr) { 602b7981f6SKowalski, Kamil res.code = static_cast<int>(HttpRespCode::NOT_FOUND); 612b7981f6SKowalski, Kamil res.end(); 622b7981f6SKowalski, Kamil return; 632b7981f6SKowalski, Kamil } 642b7981f6SKowalski, Kamil 652b7981f6SKowalski, Kamil nodeJson["Id"] = session->unique_id; 662b7981f6SKowalski, Kamil nodeJson["UserName"] = session->username; 672b7981f6SKowalski, Kamil nodeJson["@odata.id"] = 682b7981f6SKowalski, Kamil "/redfish/v1/SessionService/Sessions/" + session->unique_id; 692b7981f6SKowalski, Kamil 702b7981f6SKowalski, Kamil res.json_value = nodeJson; 712b7981f6SKowalski, Kamil res.end(); 722b7981f6SKowalski, Kamil } 732b7981f6SKowalski, Kamil 742b7981f6SKowalski, Kamil void doDelete(crow::response& res, const crow::request& req, 752b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 762b7981f6SKowalski, Kamil // Need only 1 param which should be id of session to be deleted 772b7981f6SKowalski, Kamil if (params.size() != 1) { 782b7981f6SKowalski, Kamil res.code = static_cast<int>(HttpRespCode::BAD_REQUEST); 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) { 872b7981f6SKowalski, Kamil res.code = static_cast<int>(HttpRespCode::NOT_FOUND); 882b7981f6SKowalski, Kamil res.end(); 892b7981f6SKowalski, Kamil return; 902b7981f6SKowalski, Kamil } 912b7981f6SKowalski, Kamil 922b7981f6SKowalski, Kamil crow::PersistentData::session_store->remove_session(session); 932b7981f6SKowalski, Kamil res.code = static_cast<int>(HttpRespCode::OK); 942b7981f6SKowalski, Kamil res.end(); 952b7981f6SKowalski, Kamil } 962b7981f6SKowalski, Kamil 972b7981f6SKowalski, Kamil /** 982b7981f6SKowalski, Kamil * This allows SessionCollection to reuse this class' doGet method, to 992b7981f6SKowalski, Kamil * maintain consistency of returned data, as Collection's doPost should return 1002b7981f6SKowalski, Kamil * data for created member which should match member's doGet result in 100% 1012b7981f6SKowalski, Kamil */ 1022b7981f6SKowalski, Kamil friend SessionCollection; 1032b7981f6SKowalski, Kamil 1042b7981f6SKowalski, Kamil nlohmann::json nodeJson; 1052b7981f6SKowalski, Kamil }; 1062b7981f6SKowalski, Kamil 1072b7981f6SKowalski, Kamil class SessionCollection : public Node { 1082b7981f6SKowalski, Kamil public: 109*43a095abSBorawski.Lukasz template <typename CrowApp> 110*43a095abSBorawski.Lukasz SessionCollection(CrowApp& app) 111*43a095abSBorawski.Lukasz : Node(app, EntityPrivileges(std::move(sessionCollectionOpMap)), 1122b7981f6SKowalski, Kamil "/redfish/v1/SessionService/Sessions/"), 113*43a095abSBorawski.Lukasz memberSession(app) { 114aecb47a4SBorawski.Lukasz nodeJson["@odata.type"] = "#SessionCollection.SessionCollection"; 115aecb47a4SBorawski.Lukasz nodeJson["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; 1162b7981f6SKowalski, Kamil nodeJson["@odata.context"] = 1172b7981f6SKowalski, Kamil "/redfish/v1/$metadata#SessionCollection.SessionCollection"; 1182b7981f6SKowalski, Kamil nodeJson["Name"] = "Session Collection"; 1192b7981f6SKowalski, Kamil nodeJson["Description"] = "Session Collection"; 1202b7981f6SKowalski, Kamil nodeJson["Members@odata.count"] = 0; 1212b7981f6SKowalski, Kamil nodeJson["Members"] = nlohmann::json::array(); 1222b7981f6SKowalski, Kamil } 1232b7981f6SKowalski, Kamil 1242b7981f6SKowalski, Kamil private: 1252b7981f6SKowalski, Kamil void doGet(crow::response& res, const crow::request& req, 1262b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 1272b7981f6SKowalski, Kamil std::vector<const std::string*> session_ids = 1282b7981f6SKowalski, Kamil crow::PersistentData::session_store->get_unique_ids( 1292b7981f6SKowalski, Kamil false, crow::PersistentData::PersistenceType::TIMEOUT); 1302b7981f6SKowalski, Kamil 1312b7981f6SKowalski, Kamil nodeJson["Members@odata.count"] = session_ids.size(); 1322b7981f6SKowalski, Kamil nodeJson["Members"] = nlohmann::json::array(); 1332b7981f6SKowalski, Kamil for (const auto& uid : session_ids) { 1342b7981f6SKowalski, Kamil nodeJson["Members"].push_back( 1352b7981f6SKowalski, Kamil {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); 1362b7981f6SKowalski, Kamil } 1372b7981f6SKowalski, Kamil 1382b7981f6SKowalski, Kamil res.json_value = nodeJson; 1392b7981f6SKowalski, Kamil res.end(); 1402b7981f6SKowalski, Kamil } 1412b7981f6SKowalski, Kamil 1422b7981f6SKowalski, Kamil void doPost(crow::response& res, const crow::request& req, 1432b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 1442b7981f6SKowalski, Kamil std::string username; 1452b7981f6SKowalski, Kamil bool userAuthSuccessful = authenticateUser(req, &res.code, &username); 1462b7981f6SKowalski, Kamil if (!userAuthSuccessful) { 1472b7981f6SKowalski, Kamil res.end(); 1482b7981f6SKowalski, Kamil return; 1492b7981f6SKowalski, Kamil } 1502b7981f6SKowalski, Kamil 1512b7981f6SKowalski, Kamil // User is authenticated - create session for him 1522b7981f6SKowalski, Kamil auto session = 1532b7981f6SKowalski, Kamil crow::PersistentData::session_store->generate_user_session(username); 1542b7981f6SKowalski, Kamil res.add_header("X-Auth-Token", session.session_token); 1552b7981f6SKowalski, Kamil 1562b7981f6SKowalski, Kamil // Return data for created session 1572b7981f6SKowalski, Kamil memberSession.doGet(res, req, {session.unique_id}); 1582b7981f6SKowalski, Kamil 1592b7981f6SKowalski, Kamil // No need for res.end(), as it is called by doGet() 1602b7981f6SKowalski, Kamil } 1612b7981f6SKowalski, Kamil 1622b7981f6SKowalski, Kamil /** 1632b7981f6SKowalski, Kamil * @brief Verifies data provided in request and tries to authenticate user 1642b7981f6SKowalski, Kamil * 1652b7981f6SKowalski, Kamil * @param[in] req Crow request containing authentication data 1662b7981f6SKowalski, Kamil * @param[out] httpRespCode HTTP Code that should be returned in response 1672b7981f6SKowalski, Kamil * @param[out] user Retrieved username - not filled on failure 1682b7981f6SKowalski, Kamil * 1692b7981f6SKowalski, Kamil * @return true if authentication was successful, false otherwise 1702b7981f6SKowalski, Kamil */ 1712b7981f6SKowalski, Kamil bool authenticateUser(const crow::request& req, int* httpRespCode, 1722b7981f6SKowalski, Kamil std::string* user) { 1732b7981f6SKowalski, Kamil // We need only UserName and Password - nothing more, nothing less 1742b7981f6SKowalski, Kamil static constexpr const unsigned int numberOfRequiredFieldsInReq = 2; 1752b7981f6SKowalski, Kamil 1762b7981f6SKowalski, Kamil // call with exceptions disabled 1772b7981f6SKowalski, Kamil auto login_credentials = nlohmann::json::parse(req.body, nullptr, false); 1782b7981f6SKowalski, Kamil if (login_credentials.is_discarded()) { 1792b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST); 1802b7981f6SKowalski, Kamil 1812b7981f6SKowalski, Kamil return false; 1822b7981f6SKowalski, Kamil } 1832b7981f6SKowalski, Kamil 1842b7981f6SKowalski, Kamil // Check that there are only as many fields as there should be 1852b7981f6SKowalski, Kamil if (login_credentials.size() != numberOfRequiredFieldsInReq) { 1862b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST); 1872b7981f6SKowalski, Kamil 1882b7981f6SKowalski, Kamil return false; 1892b7981f6SKowalski, Kamil } 1902b7981f6SKowalski, Kamil 1912b7981f6SKowalski, Kamil // Find fields that we need - UserName and Password 1922b7981f6SKowalski, Kamil auto user_it = login_credentials.find("UserName"); 1932b7981f6SKowalski, Kamil auto pass_it = login_credentials.find("Password"); 1942b7981f6SKowalski, Kamil if (user_it == login_credentials.end() || 1952b7981f6SKowalski, Kamil pass_it == login_credentials.end()) { 1962b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST); 1972b7981f6SKowalski, Kamil 1982b7981f6SKowalski, Kamil return false; 1992b7981f6SKowalski, Kamil } 2002b7981f6SKowalski, Kamil 2012b7981f6SKowalski, Kamil // Check that given data is of valid type (string) 2022b7981f6SKowalski, Kamil if (!user_it->is_string() || !pass_it->is_string()) { 2032b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST); 2042b7981f6SKowalski, Kamil 2052b7981f6SKowalski, Kamil return false; 2062b7981f6SKowalski, Kamil } 2072b7981f6SKowalski, Kamil 2082b7981f6SKowalski, Kamil // Extract username and password 2092b7981f6SKowalski, Kamil std::string username = user_it->get<const std::string>(); 2102b7981f6SKowalski, Kamil std::string password = pass_it->get<const std::string>(); 2112b7981f6SKowalski, Kamil 2122b7981f6SKowalski, Kamil // Verify that required fields are not empty 2132b7981f6SKowalski, Kamil if (username.empty() || password.empty()) { 2142b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST); 2152b7981f6SKowalski, Kamil 2162b7981f6SKowalski, Kamil return false; 2172b7981f6SKowalski, Kamil } 2182b7981f6SKowalski, Kamil 2192b7981f6SKowalski, Kamil // Finally - try to authenticate user 2202b7981f6SKowalski, Kamil if (!pam_authenticate_user(username, password)) { 2212b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::UNAUTHORIZED); 2222b7981f6SKowalski, Kamil 2232b7981f6SKowalski, Kamil return false; 2242b7981f6SKowalski, Kamil } 2252b7981f6SKowalski, Kamil 2262b7981f6SKowalski, Kamil // User authenticated successfully 2272b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::OK); 2282b7981f6SKowalski, Kamil *user = username; 2292b7981f6SKowalski, Kamil 2302b7981f6SKowalski, Kamil return true; 2312b7981f6SKowalski, Kamil } 2322b7981f6SKowalski, Kamil 2332b7981f6SKowalski, Kamil /** 2342b7981f6SKowalski, Kamil * Member session to ensure consistency between collection's doPost and 2352b7981f6SKowalski, Kamil * member's doGet, as they should return 100% matching data 2362b7981f6SKowalski, Kamil */ 2372b7981f6SKowalski, Kamil Sessions memberSession; 2382b7981f6SKowalski, Kamil nlohmann::json nodeJson; 2392b7981f6SKowalski, Kamil }; 2402b7981f6SKowalski, Kamil 2412b7981f6SKowalski, Kamil } // namespace redfish 242