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