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