| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 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 | 
| Borawski.Lukasz | 43a095a | 2018-02-19 15:39:01 +0100 | [diff] [blame] | 17 |  | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 18 | #include "node.hpp" | 
 | 19 | #include "session_storage_singleton.hpp" | 
 | 20 |  | 
 | 21 | namespace redfish { | 
 | 22 |  | 
| Borawski.Lukasz | 43a095a | 2018-02-19 15:39:01 +0100 | [diff] [blame] | 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 |  | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 39 | class SessionCollection; | 
 | 40 |  | 
 | 41 | class Sessions : public Node { | 
 | 42 |  public: | 
| Borawski.Lukasz | 43a095a | 2018-02-19 15:39:01 +0100 | [diff] [blame] | 43 |   Sessions(CrowApp& app) | 
 | 44 |       : Node(app, EntityPrivileges(std::move(sessionOpMap)), | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 45 |              "/redfish/v1/SessionService/Sessions/<str>", std::string()) { | 
| Borawski.Lukasz | c1a46bd | 2018-02-08 13:31:59 +0100 | [diff] [blame^] | 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"; | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 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 |  | 
| Borawski.Lukasz | c1a46bd | 2018-02-08 13:31:59 +0100 | [diff] [blame^] | 64 |     Node::json["Id"] = session->unique_id; | 
 | 65 |     Node::json["UserName"] = session->username; | 
 | 66 |     Node::json["@odata.id"] = | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 67 |         "/redfish/v1/SessionService/Sessions/" + session->unique_id; | 
 | 68 |  | 
| Borawski.Lukasz | c1a46bd | 2018-02-08 13:31:59 +0100 | [diff] [blame^] | 69 |     res.json_value = Node::json; | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 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; | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 102 | }; | 
 | 103 |  | 
 | 104 | class SessionCollection : public Node { | 
 | 105 |  public: | 
| Borawski.Lukasz | 43a095a | 2018-02-19 15:39:01 +0100 | [diff] [blame] | 106 |   SessionCollection(CrowApp& app) | 
 | 107 |       : Node(app, EntityPrivileges(std::move(sessionCollectionOpMap)), | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 108 |              "/redfish/v1/SessionService/Sessions/"), | 
| Borawski.Lukasz | 43a095a | 2018-02-19 15:39:01 +0100 | [diff] [blame] | 109 |         memberSession(app) { | 
| Borawski.Lukasz | c1a46bd | 2018-02-08 13:31:59 +0100 | [diff] [blame^] | 110 |     Node::json["@odata.type"] = "#SessionCollection.SessionCollection"; | 
 | 111 |     Node::json["@odata.id"] = "/redfish/v1/SessionService/Sessions/"; | 
 | 112 |     Node::json["@odata.context"] = | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 113 |         "/redfish/v1/$metadata#SessionCollection.SessionCollection"; | 
| Borawski.Lukasz | c1a46bd | 2018-02-08 13:31:59 +0100 | [diff] [blame^] | 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(); | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 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 |  | 
| Borawski.Lukasz | c1a46bd | 2018-02-08 13:31:59 +0100 | [diff] [blame^] | 127 |     Node::json["Members@odata.count"] = session_ids.size(); | 
 | 128 |     Node::json["Members"] = nlohmann::json::array(); | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 129 |     for (const auto& uid : session_ids) { | 
| Borawski.Lukasz | c1a46bd | 2018-02-08 13:31:59 +0100 | [diff] [blame^] | 130 |       Node::json["Members"].push_back( | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 131 |           {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}}); | 
 | 132 |     } | 
 | 133 |  | 
| Borawski.Lukasz | c1a46bd | 2018-02-08 13:31:59 +0100 | [diff] [blame^] | 134 |     res.json_value = Node::json; | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 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); | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 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; | 
| Kowalski, Kamil | 2b7981f | 2018-01-31 13:24:59 +0100 | [diff] [blame] | 234 | }; | 
 | 235 |  | 
 | 236 | }  // namespace redfish |