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