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 22*1abe55efSEd Tanous namespace redfish 23*1abe55efSEd Tanous { 242b7981f6SKowalski, Kamil 252b7981f6SKowalski, Kamil class SessionCollection; 262b7981f6SKowalski, Kamil 27*1abe55efSEd Tanous class Sessions : public Node 28*1abe55efSEd Tanous { 292b7981f6SKowalski, Kamil public: 30*1abe55efSEd Tanous Sessions(CrowApp& app) : 31*1abe55efSEd Tanous Node(app, "/redfish/v1/SessionService/Sessions/<str>/", std::string()) 32*1abe55efSEd 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, 49*1abe55efSEd Tanous const std::vector<std::string>& params) override 50*1abe55efSEd Tanous { 512b7981f6SKowalski, Kamil auto session = 5255c7b7a2SEd Tanous crow::persistent_data::SessionStore::getInstance().getSessionByUid( 534b1b8683SBorawski.Lukasz params[0]); 542b7981f6SKowalski, Kamil 55*1abe55efSEd Tanous if (session == nullptr) 56*1abe55efSEd Tanous { 57f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 58*1abe55efSEd Tanous res.jsonValue, 59*1abe55efSEd 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, 76*1abe55efSEd Tanous const std::vector<std::string>& params) override 77*1abe55efSEd Tanous { 782b7981f6SKowalski, Kamil // Need only 1 param which should be id of session to be deleted 79*1abe55efSEd Tanous if (params.size() != 1) 80*1abe55efSEd Tanous { 81f4c4dcf4SKowalski, Kamil // This should be handled by crow and never happen 82*1abe55efSEd Tanous BMCWEB_LOG_ERROR << "Session DELETE has been called with invalid " 83*1abe55efSEd Tanous "number of params"; 84f4c4dcf4SKowalski, Kamil 85e0d918bcSEd Tanous res.result(boost::beast::http::status::bad_request); 86*1abe55efSEd Tanous messages::addMessageToErrorJson(res.jsonValue, 87*1abe55efSEd 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 97*1abe55efSEd Tanous if (session == nullptr) 98*1abe55efSEd Tanous { 99f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 100*1abe55efSEd Tanous res.jsonValue, 101*1abe55efSEd 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 111*1abe55efSEd Tanous crow::persistent_data::SessionStore::getInstance().removeSession( 112*1abe55efSEd Tanous session); 1132b7981f6SKowalski, Kamil } 1142b7981f6SKowalski, Kamil 1152b7981f6SKowalski, Kamil /** 1162b7981f6SKowalski, Kamil * This allows SessionCollection to reuse this class' doGet method, to 117*1abe55efSEd Tanous * maintain consistency of returned data, as Collection's doPost should 118*1abe55efSEd Tanous * return data for created member which should match member's doGet result 119*1abe55efSEd Tanous * in 100% 1202b7981f6SKowalski, Kamil */ 1212b7981f6SKowalski, Kamil friend SessionCollection; 1222b7981f6SKowalski, Kamil }; 1232b7981f6SKowalski, Kamil 124*1abe55efSEd Tanous class SessionCollection : public Node 125*1abe55efSEd Tanous { 1262b7981f6SKowalski, Kamil public: 127*1abe55efSEd Tanous SessionCollection(CrowApp& app) : 128*1abe55efSEd Tanous Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app) 129*1abe55efSEd 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, 150*1abe55efSEd Tanous const std::vector<std::string>& params) override 151*1abe55efSEd 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(); 158*1abe55efSEd Tanous for (const std::string* uid : sessionIds) 159*1abe55efSEd 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, 169*1abe55efSEd Tanous const std::vector<std::string>& params) override 170*1abe55efSEd Tanous { 171e0d918bcSEd Tanous boost::beast::http::status status; 1722b7981f6SKowalski, Kamil std::string username; 173f4c4dcf4SKowalski, Kamil bool userAuthSuccessful = 17455c7b7a2SEd Tanous authenticateUser(req, status, username, res.jsonValue); 175e0d918bcSEd Tanous res.result(status); 176e0d918bcSEd Tanous 177*1abe55efSEd Tanous if (!userAuthSuccessful) 178*1abe55efSEd Tanous { 1792b7981f6SKowalski, Kamil res.end(); 1802b7981f6SKowalski, Kamil return; 1812b7981f6SKowalski, Kamil } 1822b7981f6SKowalski, Kamil 1832b7981f6SKowalski, Kamil // User is authenticated - create session for him 184*1abe55efSEd Tanous auto session = crow::persistent_data::SessionStore::getInstance() 185*1abe55efSEd Tanous .generateUserSession(username); 18655c7b7a2SEd Tanous res.addHeader("X-Auth-Token", session->sessionToken); 1872b7981f6SKowalski, Kamil 188*1abe55efSEd Tanous res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" + 189*1abe55efSEd Tanous session->uniqueId); 190b9845d9eSEd Tanous 1912b7981f6SKowalski, Kamil // Return data for created session 19255c7b7a2SEd Tanous memberSession.doGet(res, req, {session->uniqueId}); 1932b7981f6SKowalski, Kamil 1942b7981f6SKowalski, Kamil // No need for res.end(), as it is called by doGet() 1952b7981f6SKowalski, Kamil } 1962b7981f6SKowalski, Kamil 1972b7981f6SKowalski, Kamil /** 1982b7981f6SKowalski, Kamil * @brief Verifies data provided in request and tries to authenticate user 1992b7981f6SKowalski, Kamil * 2002b7981f6SKowalski, Kamil * @param[in] req Crow request containing authentication data 2012b7981f6SKowalski, Kamil * @param[out] httpRespCode HTTP Code that should be returned in response 2022b7981f6SKowalski, Kamil * @param[out] user Retrieved username - not filled on failure 203f4c4dcf4SKowalski, Kamil * @param[out] errJson JSON to which error messages will be written 2042b7981f6SKowalski, Kamil * 2052b7981f6SKowalski, Kamil * @return true if authentication was successful, false otherwise 2062b7981f6SKowalski, Kamil */ 20755c7b7a2SEd Tanous bool authenticateUser(const crow::Request& req, 208e0d918bcSEd Tanous boost::beast::http::status& httpRespCode, 209*1abe55efSEd Tanous std::string& user, nlohmann::json& errJson) 210*1abe55efSEd Tanous { 2112b7981f6SKowalski, Kamil // We need only UserName and Password - nothing more, nothing less 2122b7981f6SKowalski, Kamil static constexpr const unsigned int numberOfRequiredFieldsInReq = 2; 2132b7981f6SKowalski, Kamil 2142b7981f6SKowalski, Kamil // call with exceptions disabled 21555c7b7a2SEd Tanous auto loginCredentials = nlohmann::json::parse(req.body, nullptr, false); 216*1abe55efSEd Tanous if (loginCredentials.is_discarded()) 217*1abe55efSEd Tanous { 218e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 219f4c4dcf4SKowalski, Kamil 220f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, messages::malformedJSON()); 2212b7981f6SKowalski, Kamil 2222b7981f6SKowalski, Kamil return false; 2232b7981f6SKowalski, Kamil } 2242b7981f6SKowalski, Kamil 2252b7981f6SKowalski, Kamil // Check that there are only as many fields as there should be 226*1abe55efSEd Tanous if (loginCredentials.size() != numberOfRequiredFieldsInReq) 227*1abe55efSEd Tanous { 228e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 229f4c4dcf4SKowalski, Kamil 230f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson(errJson, messages::malformedJSON()); 2312b7981f6SKowalski, Kamil 2322b7981f6SKowalski, Kamil return false; 2332b7981f6SKowalski, Kamil } 2342b7981f6SKowalski, Kamil 2352b7981f6SKowalski, Kamil // Find fields that we need - UserName and Password 23655c7b7a2SEd Tanous auto userIt = loginCredentials.find("UserName"); 23755c7b7a2SEd Tanous auto passIt = loginCredentials.find("Password"); 238*1abe55efSEd Tanous if (userIt == loginCredentials.end() || 239*1abe55efSEd Tanous passIt == loginCredentials.end()) 240*1abe55efSEd Tanous { 241e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 242f4c4dcf4SKowalski, Kamil 243*1abe55efSEd Tanous if (userIt == loginCredentials.end()) 244*1abe55efSEd Tanous { 245*1abe55efSEd Tanous messages::addMessageToErrorJson( 246*1abe55efSEd Tanous errJson, messages::propertyMissing("UserName")); 247f4c4dcf4SKowalski, Kamil } 248f4c4dcf4SKowalski, Kamil 249*1abe55efSEd Tanous if (passIt == loginCredentials.end()) 250*1abe55efSEd Tanous { 251*1abe55efSEd Tanous messages::addMessageToErrorJson( 252*1abe55efSEd Tanous errJson, messages::propertyMissing("Password")); 253f4c4dcf4SKowalski, Kamil } 2542b7981f6SKowalski, Kamil 2552b7981f6SKowalski, Kamil return false; 2562b7981f6SKowalski, Kamil } 2572b7981f6SKowalski, Kamil 2582b7981f6SKowalski, Kamil // Check that given data is of valid type (string) 259*1abe55efSEd Tanous if (!userIt->is_string() || !passIt->is_string()) 260*1abe55efSEd Tanous { 261e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 262f4c4dcf4SKowalski, Kamil 263*1abe55efSEd Tanous if (!userIt->is_string()) 264*1abe55efSEd Tanous { 265f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 266*1abe55efSEd Tanous errJson, messages::propertyValueTypeError(userIt->dump(), 267*1abe55efSEd Tanous "UserName")); 268f4c4dcf4SKowalski, Kamil } 269f4c4dcf4SKowalski, Kamil 270*1abe55efSEd Tanous if (!passIt->is_string()) 271*1abe55efSEd Tanous { 272f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 273*1abe55efSEd Tanous errJson, messages::propertyValueTypeError(userIt->dump(), 274*1abe55efSEd Tanous "Password")); 275f4c4dcf4SKowalski, Kamil } 2762b7981f6SKowalski, Kamil 2772b7981f6SKowalski, Kamil return false; 2782b7981f6SKowalski, Kamil } 2792b7981f6SKowalski, Kamil 2802b7981f6SKowalski, Kamil // Extract username and password 28155c7b7a2SEd Tanous std::string username = userIt->get<const std::string>(); 28255c7b7a2SEd Tanous std::string password = passIt->get<const std::string>(); 2832b7981f6SKowalski, Kamil 2842b7981f6SKowalski, Kamil // Verify that required fields are not empty 285*1abe55efSEd Tanous if (username.empty() || password.empty()) 286*1abe55efSEd Tanous { 287e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::bad_request; 288f4c4dcf4SKowalski, Kamil 289*1abe55efSEd Tanous if (username.empty()) 290*1abe55efSEd Tanous { 291*1abe55efSEd Tanous messages::addMessageToErrorJson( 292*1abe55efSEd Tanous errJson, messages::propertyMissing("UserName")); 293f4c4dcf4SKowalski, Kamil } 294f4c4dcf4SKowalski, Kamil 295*1abe55efSEd Tanous if (password.empty()) 296*1abe55efSEd Tanous { 297*1abe55efSEd Tanous messages::addMessageToErrorJson( 298*1abe55efSEd Tanous errJson, messages::propertyMissing("Password")); 299f4c4dcf4SKowalski, Kamil } 3002b7981f6SKowalski, Kamil 3012b7981f6SKowalski, Kamil return false; 3022b7981f6SKowalski, Kamil } 3032b7981f6SKowalski, Kamil 3042b7981f6SKowalski, Kamil // Finally - try to authenticate user 305*1abe55efSEd Tanous if (!pamAuthenticateUser(username, password)) 306*1abe55efSEd Tanous { 307e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::unauthorized; 308f4c4dcf4SKowalski, Kamil 309f4c4dcf4SKowalski, Kamil messages::addMessageToErrorJson( 310*1abe55efSEd Tanous errJson, 311*1abe55efSEd Tanous messages::resourceAtUriUnauthorized( 312e0d918bcSEd Tanous std::string(req.url), "Invalid username or password")); 3132b7981f6SKowalski, Kamil 3142b7981f6SKowalski, Kamil return false; 3152b7981f6SKowalski, Kamil } 3162b7981f6SKowalski, Kamil 3172b7981f6SKowalski, Kamil // User authenticated successfully 318e0d918bcSEd Tanous httpRespCode = boost::beast::http::status::ok; 319f4c4dcf4SKowalski, Kamil user = username; 3202b7981f6SKowalski, Kamil 3212b7981f6SKowalski, Kamil return true; 3222b7981f6SKowalski, Kamil } 3232b7981f6SKowalski, Kamil 3242b7981f6SKowalski, Kamil /** 3252b7981f6SKowalski, Kamil * Member session to ensure consistency between collection's doPost and 3262b7981f6SKowalski, Kamil * member's doGet, as they should return 100% matching data 3272b7981f6SKowalski, Kamil */ 3282b7981f6SKowalski, Kamil Sessions memberSession; 3292b7981f6SKowalski, Kamil }; 3302b7981f6SKowalski, Kamil 331*1abe55efSEd Tanous class SessionService : public Node 332*1abe55efSEd Tanous { 3335d27b854SBorawski.Lukasz public: 334*1abe55efSEd Tanous SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/") 335*1abe55efSEd Tanous { 3365d27b854SBorawski.Lukasz Node::json["@odata.type"] = "#SessionService.v1_0_2.SessionService"; 3375d27b854SBorawski.Lukasz Node::json["@odata.id"] = "/redfish/v1/SessionService/"; 3385d27b854SBorawski.Lukasz Node::json["@odata.context"] = 3395d27b854SBorawski.Lukasz "/redfish/v1/$metadata#SessionService.SessionService"; 3405d27b854SBorawski.Lukasz Node::json["Name"] = "Session Service"; 3416c233015SEd Tanous Node::json["Id"] = "SessionService"; 3425d27b854SBorawski.Lukasz Node::json["Description"] = "Session Service"; 3435d27b854SBorawski.Lukasz Node::json["SessionTimeout"] = 34455c7b7a2SEd Tanous crow::persistent_data::SessionStore::getInstance() 34555c7b7a2SEd Tanous .getTimeoutInSeconds(); 3465d27b854SBorawski.Lukasz Node::json["ServiceEnabled"] = true; 3473ebd75f7SEd Tanous 348e0d918bcSEd Tanous entityPrivileges = { 349e0d918bcSEd Tanous {boost::beast::http::verb::get, {{"Login"}}}, 350e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 351e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 352e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 353e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 354e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 3555d27b854SBorawski.Lukasz } 3565d27b854SBorawski.Lukasz 3575d27b854SBorawski.Lukasz private: 35855c7b7a2SEd Tanous void doGet(crow::Response& res, const crow::Request& req, 359*1abe55efSEd Tanous const std::vector<std::string>& params) override 360*1abe55efSEd Tanous { 36155c7b7a2SEd Tanous res.jsonValue = Node::json; 3625d27b854SBorawski.Lukasz res.end(); 3635d27b854SBorawski.Lukasz } 3645d27b854SBorawski.Lukasz }; 3655d27b854SBorawski.Lukasz 3662b7981f6SKowalski, Kamil } // namespace redfish 367