1*2b7981f6SKowalski, Kamil /* 2*2b7981f6SKowalski, Kamil // Copyright (c) 2018 Intel Corporation 3*2b7981f6SKowalski, Kamil // 4*2b7981f6SKowalski, Kamil // Licensed under the Apache License, Version 2.0 (the "License"); 5*2b7981f6SKowalski, Kamil // you may not use this file except in compliance with the License. 6*2b7981f6SKowalski, Kamil // You may obtain a copy of the License at 7*2b7981f6SKowalski, Kamil // 8*2b7981f6SKowalski, Kamil // http://www.apache.org/licenses/LICENSE-2.0 9*2b7981f6SKowalski, Kamil // 10*2b7981f6SKowalski, Kamil // Unless required by applicable law or agreed to in writing, software 11*2b7981f6SKowalski, Kamil // distributed under the License is distributed on an "AS IS" BASIS, 12*2b7981f6SKowalski, Kamil // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*2b7981f6SKowalski, Kamil // See the License for the specific language governing permissions and 14*2b7981f6SKowalski, Kamil // limitations under the License. 15*2b7981f6SKowalski, Kamil */ 16*2b7981f6SKowalski, Kamil #pragma once 17*2b7981f6SKowalski, Kamil #include <tuple> 18*2b7981f6SKowalski, Kamil #include "node.hpp" 19*2b7981f6SKowalski, Kamil #include "session_storage_singleton.hpp" 20*2b7981f6SKowalski, Kamil 21*2b7981f6SKowalski, Kamil namespace redfish { 22*2b7981f6SKowalski, Kamil 23*2b7981f6SKowalski, Kamil class SessionCollection; 24*2b7981f6SKowalski, Kamil 25*2b7981f6SKowalski, Kamil class Sessions : public Node { 26*2b7981f6SKowalski, Kamil public: 27*2b7981f6SKowalski, Kamil template <typename CrowApp, typename PrivilegeProvider> 28*2b7981f6SKowalski, Kamil Sessions(CrowApp& app, PrivilegeProvider& provider) 29*2b7981f6SKowalski, Kamil : Node(app, provider, "#Session.v1_0_2.Session", 30*2b7981f6SKowalski, Kamil "/redfish/v1/SessionService/Sessions/<str>", std::string()) { 31*2b7981f6SKowalski, Kamil nodeJson["@odata.type"] = Node::odataType; 32*2b7981f6SKowalski, Kamil nodeJson["@odata.context"] = "/redfish/v1/$metadata#Session.Session"; 33*2b7981f6SKowalski, Kamil nodeJson["Name"] = "User Session"; 34*2b7981f6SKowalski, Kamil nodeJson["Description"] = "Manager User Session"; 35*2b7981f6SKowalski, Kamil } 36*2b7981f6SKowalski, Kamil 37*2b7981f6SKowalski, Kamil private: 38*2b7981f6SKowalski, Kamil void doGet(crow::response& res, const crow::request& req, 39*2b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 40*2b7981f6SKowalski, Kamil auto session = 41*2b7981f6SKowalski, Kamil crow::PersistentData::session_store->get_session_by_uid(params[0]); 42*2b7981f6SKowalski, Kamil 43*2b7981f6SKowalski, Kamil if (session == nullptr) { 44*2b7981f6SKowalski, Kamil res.code = static_cast<int>(HttpRespCode::NOT_FOUND); 45*2b7981f6SKowalski, Kamil res.end(); 46*2b7981f6SKowalski, Kamil return; 47*2b7981f6SKowalski, Kamil } 48*2b7981f6SKowalski, Kamil 49*2b7981f6SKowalski, Kamil nodeJson["Id"] = session->unique_id; 50*2b7981f6SKowalski, Kamil nodeJson["UserName"] = session->username; 51*2b7981f6SKowalski, Kamil nodeJson["@odata.id"] = 52*2b7981f6SKowalski, Kamil "/redfish/v1/SessionService/Sessions/" + session->unique_id; 53*2b7981f6SKowalski, Kamil 54*2b7981f6SKowalski, Kamil res.json_value = nodeJson; 55*2b7981f6SKowalski, Kamil res.end(); 56*2b7981f6SKowalski, Kamil } 57*2b7981f6SKowalski, Kamil 58*2b7981f6SKowalski, Kamil void doDelete(crow::response& res, const crow::request& req, 59*2b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 60*2b7981f6SKowalski, Kamil // Need only 1 param which should be id of session to be deleted 61*2b7981f6SKowalski, Kamil if (params.size() != 1) { 62*2b7981f6SKowalski, Kamil res.code = static_cast<int>(HttpRespCode::BAD_REQUEST); 63*2b7981f6SKowalski, Kamil res.end(); 64*2b7981f6SKowalski, Kamil return; 65*2b7981f6SKowalski, Kamil } 66*2b7981f6SKowalski, Kamil 67*2b7981f6SKowalski, Kamil auto session = 68*2b7981f6SKowalski, Kamil crow::PersistentData::session_store->get_session_by_uid(params[0]); 69*2b7981f6SKowalski, Kamil 70*2b7981f6SKowalski, Kamil if (session == nullptr) { 71*2b7981f6SKowalski, Kamil res.code = static_cast<int>(HttpRespCode::NOT_FOUND); 72*2b7981f6SKowalski, Kamil res.end(); 73*2b7981f6SKowalski, Kamil return; 74*2b7981f6SKowalski, Kamil } 75*2b7981f6SKowalski, Kamil 76*2b7981f6SKowalski, Kamil crow::PersistentData::session_store->remove_session(session); 77*2b7981f6SKowalski, Kamil res.code = static_cast<int>(HttpRespCode::OK); 78*2b7981f6SKowalski, Kamil res.end(); 79*2b7981f6SKowalski, Kamil } 80*2b7981f6SKowalski, Kamil 81*2b7981f6SKowalski, Kamil /** 82*2b7981f6SKowalski, Kamil * This allows SessionCollection to reuse this class' doGet method, to 83*2b7981f6SKowalski, Kamil * maintain consistency of returned data, as Collection's doPost should return 84*2b7981f6SKowalski, Kamil * data for created member which should match member's doGet result in 100% 85*2b7981f6SKowalski, Kamil */ 86*2b7981f6SKowalski, Kamil friend SessionCollection; 87*2b7981f6SKowalski, Kamil 88*2b7981f6SKowalski, Kamil nlohmann::json nodeJson; 89*2b7981f6SKowalski, Kamil }; 90*2b7981f6SKowalski, Kamil 91*2b7981f6SKowalski, Kamil class SessionCollection : public Node { 92*2b7981f6SKowalski, Kamil public: 93*2b7981f6SKowalski, Kamil template <typename CrowApp, typename PrivilegeProvider> 94*2b7981f6SKowalski, Kamil SessionCollection(CrowApp& app, PrivilegeProvider& provider) 95*2b7981f6SKowalski, Kamil : Node(app, provider, "#SessionCollection.SessionCollection", 96*2b7981f6SKowalski, Kamil "/redfish/v1/SessionService/Sessions/"), 97*2b7981f6SKowalski, Kamil memberSession(app, provider) { 98*2b7981f6SKowalski, Kamil nodeJson["@odata.type"] = Node::odataType; 99*2b7981f6SKowalski, Kamil nodeJson["@odata.id"] = Node::odataId; 100*2b7981f6SKowalski, Kamil nodeJson["@odata.context"] = 101*2b7981f6SKowalski, Kamil "/redfish/v1/$metadata#SessionCollection.SessionCollection"; 102*2b7981f6SKowalski, Kamil nodeJson["Name"] = "Session Collection"; 103*2b7981f6SKowalski, Kamil nodeJson["Description"] = "Session Collection"; 104*2b7981f6SKowalski, Kamil nodeJson["Members@odata.count"] = 0; 105*2b7981f6SKowalski, Kamil nodeJson["Members"] = nlohmann::json::array(); 106*2b7981f6SKowalski, Kamil } 107*2b7981f6SKowalski, Kamil 108*2b7981f6SKowalski, Kamil private: 109*2b7981f6SKowalski, Kamil void doGet(crow::response& res, const crow::request& req, 110*2b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 111*2b7981f6SKowalski, Kamil std::vector<const std::string*> session_ids = 112*2b7981f6SKowalski, Kamil crow::PersistentData::session_store->get_unique_ids( 113*2b7981f6SKowalski, Kamil false, crow::PersistentData::PersistenceType::TIMEOUT); 114*2b7981f6SKowalski, Kamil 115*2b7981f6SKowalski, Kamil nodeJson["Members@odata.count"] = session_ids.size(); 116*2b7981f6SKowalski, Kamil nodeJson["Members"] = nlohmann::json::array(); 117*2b7981f6SKowalski, Kamil for (const auto& uid : session_ids) { 118*2b7981f6SKowalski, Kamil nodeJson["Members"].push_back( 119*2b7981f6SKowalski, Kamil {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); 120*2b7981f6SKowalski, Kamil } 121*2b7981f6SKowalski, Kamil 122*2b7981f6SKowalski, Kamil res.json_value = nodeJson; 123*2b7981f6SKowalski, Kamil res.end(); 124*2b7981f6SKowalski, Kamil } 125*2b7981f6SKowalski, Kamil 126*2b7981f6SKowalski, Kamil void doPost(crow::response& res, const crow::request& req, 127*2b7981f6SKowalski, Kamil const std::vector<std::string>& params) override { 128*2b7981f6SKowalski, Kamil std::string username; 129*2b7981f6SKowalski, Kamil bool userAuthSuccessful = authenticateUser(req, &res.code, &username); 130*2b7981f6SKowalski, Kamil 131*2b7981f6SKowalski, Kamil if (!userAuthSuccessful) { 132*2b7981f6SKowalski, Kamil res.end(); 133*2b7981f6SKowalski, Kamil return; 134*2b7981f6SKowalski, Kamil } 135*2b7981f6SKowalski, Kamil 136*2b7981f6SKowalski, Kamil // User is authenticated - create session for him 137*2b7981f6SKowalski, Kamil auto session = 138*2b7981f6SKowalski, Kamil crow::PersistentData::session_store->generate_user_session(username); 139*2b7981f6SKowalski, Kamil res.add_header("X-Auth-Token", session.session_token); 140*2b7981f6SKowalski, Kamil 141*2b7981f6SKowalski, Kamil // Return data for created session 142*2b7981f6SKowalski, Kamil memberSession.doGet(res, req, {session.unique_id}); 143*2b7981f6SKowalski, Kamil 144*2b7981f6SKowalski, Kamil // No need for res.end(), as it is called by doGet() 145*2b7981f6SKowalski, Kamil } 146*2b7981f6SKowalski, Kamil 147*2b7981f6SKowalski, Kamil /** 148*2b7981f6SKowalski, Kamil * @brief Verifies data provided in request and tries to authenticate user 149*2b7981f6SKowalski, Kamil * 150*2b7981f6SKowalski, Kamil * @param[in] req Crow request containing authentication data 151*2b7981f6SKowalski, Kamil * @param[out] httpRespCode HTTP Code that should be returned in response 152*2b7981f6SKowalski, Kamil * @param[out] user Retrieved username - not filled on failure 153*2b7981f6SKowalski, Kamil * 154*2b7981f6SKowalski, Kamil * @return true if authentication was successful, false otherwise 155*2b7981f6SKowalski, Kamil */ 156*2b7981f6SKowalski, Kamil bool authenticateUser(const crow::request& req, int* httpRespCode, 157*2b7981f6SKowalski, Kamil std::string* user) { 158*2b7981f6SKowalski, Kamil // We need only UserName and Password - nothing more, nothing less 159*2b7981f6SKowalski, Kamil static constexpr const unsigned int numberOfRequiredFieldsInReq = 2; 160*2b7981f6SKowalski, Kamil 161*2b7981f6SKowalski, Kamil // call with exceptions disabled 162*2b7981f6SKowalski, Kamil auto login_credentials = nlohmann::json::parse(req.body, nullptr, false); 163*2b7981f6SKowalski, Kamil if (login_credentials.is_discarded()) { 164*2b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST); 165*2b7981f6SKowalski, Kamil 166*2b7981f6SKowalski, Kamil return false; 167*2b7981f6SKowalski, Kamil } 168*2b7981f6SKowalski, Kamil 169*2b7981f6SKowalski, Kamil // Check that there are only as many fields as there should be 170*2b7981f6SKowalski, Kamil if (login_credentials.size() != numberOfRequiredFieldsInReq) { 171*2b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST); 172*2b7981f6SKowalski, Kamil 173*2b7981f6SKowalski, Kamil return false; 174*2b7981f6SKowalski, Kamil } 175*2b7981f6SKowalski, Kamil 176*2b7981f6SKowalski, Kamil // Find fields that we need - UserName and Password 177*2b7981f6SKowalski, Kamil auto user_it = login_credentials.find("UserName"); 178*2b7981f6SKowalski, Kamil auto pass_it = login_credentials.find("Password"); 179*2b7981f6SKowalski, Kamil if (user_it == login_credentials.end() || 180*2b7981f6SKowalski, Kamil pass_it == login_credentials.end()) { 181*2b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST); 182*2b7981f6SKowalski, Kamil 183*2b7981f6SKowalski, Kamil return false; 184*2b7981f6SKowalski, Kamil } 185*2b7981f6SKowalski, Kamil 186*2b7981f6SKowalski, Kamil // Check that given data is of valid type (string) 187*2b7981f6SKowalski, Kamil if (!user_it->is_string() || !pass_it->is_string()) { 188*2b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST); 189*2b7981f6SKowalski, Kamil 190*2b7981f6SKowalski, Kamil return false; 191*2b7981f6SKowalski, Kamil } 192*2b7981f6SKowalski, Kamil 193*2b7981f6SKowalski, Kamil // Extract username and password 194*2b7981f6SKowalski, Kamil std::string username = user_it->get<const std::string>(); 195*2b7981f6SKowalski, Kamil std::string password = pass_it->get<const std::string>(); 196*2b7981f6SKowalski, Kamil 197*2b7981f6SKowalski, Kamil // Verify that required fields are not empty 198*2b7981f6SKowalski, Kamil if (username.empty() || password.empty()) { 199*2b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST); 200*2b7981f6SKowalski, Kamil 201*2b7981f6SKowalski, Kamil return false; 202*2b7981f6SKowalski, Kamil } 203*2b7981f6SKowalski, Kamil 204*2b7981f6SKowalski, Kamil // Finally - try to authenticate user 205*2b7981f6SKowalski, Kamil if (!pam_authenticate_user(username, password)) { 206*2b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::UNAUTHORIZED); 207*2b7981f6SKowalski, Kamil 208*2b7981f6SKowalski, Kamil return false; 209*2b7981f6SKowalski, Kamil } 210*2b7981f6SKowalski, Kamil 211*2b7981f6SKowalski, Kamil // User authenticated successfully 212*2b7981f6SKowalski, Kamil *httpRespCode = static_cast<int>(HttpRespCode::OK); 213*2b7981f6SKowalski, Kamil *user = username; 214*2b7981f6SKowalski, Kamil 215*2b7981f6SKowalski, Kamil return true; 216*2b7981f6SKowalski, Kamil } 217*2b7981f6SKowalski, Kamil 218*2b7981f6SKowalski, Kamil /** 219*2b7981f6SKowalski, Kamil * Member session to ensure consistency between collection's doPost and 220*2b7981f6SKowalski, Kamil * member's doGet, as they should return 100% matching data 221*2b7981f6SKowalski, Kamil */ 222*2b7981f6SKowalski, Kamil Sessions memberSession; 223*2b7981f6SKowalski, Kamil nlohmann::json nodeJson; 224*2b7981f6SKowalski, Kamil }; 225*2b7981f6SKowalski, Kamil 226*2b7981f6SKowalski, Kamil } // namespace redfish 227