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