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