xref: /openbmc/bmcweb/features/redfish/lib/redfish_sessions.hpp (revision 43a095ab36b35bcb9192218fd11e0ff86ec63bf9)
12b7981f6SKowalski, Kamil /*
22b7981f6SKowalski, Kamil // Copyright (c) 2018 Intel Corporation
32b7981f6SKowalski, Kamil //
42b7981f6SKowalski, Kamil // Licensed under the Apache License, Version 2.0 (the "License");
52b7981f6SKowalski, Kamil // you may not use this file except in compliance with the License.
62b7981f6SKowalski, Kamil // You may obtain a copy of the License at
72b7981f6SKowalski, Kamil //
82b7981f6SKowalski, Kamil //      http://www.apache.org/licenses/LICENSE-2.0
92b7981f6SKowalski, Kamil //
102b7981f6SKowalski, Kamil // Unless required by applicable law or agreed to in writing, software
112b7981f6SKowalski, Kamil // distributed under the License is distributed on an "AS IS" BASIS,
122b7981f6SKowalski, Kamil // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132b7981f6SKowalski, Kamil // See the License for the specific language governing permissions and
142b7981f6SKowalski, Kamil // limitations under the License.
152b7981f6SKowalski, Kamil */
162b7981f6SKowalski, Kamil #pragma once
17*43a095abSBorawski.Lukasz 
182b7981f6SKowalski, Kamil #include "node.hpp"
192b7981f6SKowalski, Kamil #include "session_storage_singleton.hpp"
202b7981f6SKowalski, Kamil 
212b7981f6SKowalski, Kamil namespace redfish {
222b7981f6SKowalski, Kamil 
23*43a095abSBorawski.Lukasz static OperationMap sessionOpMap = {
24*43a095abSBorawski.Lukasz     {crow::HTTPMethod::GET, {{"Login"}}},
25*43a095abSBorawski.Lukasz     {crow::HTTPMethod::HEAD, {{"Login"}}},
26*43a095abSBorawski.Lukasz     {crow::HTTPMethod::PATCH, {{"ConfigureManager"}}},
27*43a095abSBorawski.Lukasz     {crow::HTTPMethod::PUT, {{"ConfigureManager"}}},
28*43a095abSBorawski.Lukasz     {crow::HTTPMethod::DELETE, {{"ConfigureManager"}}},
29*43a095abSBorawski.Lukasz     {crow::HTTPMethod::POST, {{"ConfigureManager"}}}};
30*43a095abSBorawski.Lukasz 
31*43a095abSBorawski.Lukasz static OperationMap sessionCollectionOpMap = {
32*43a095abSBorawski.Lukasz     {crow::HTTPMethod::GET, {{"Login"}}},
33*43a095abSBorawski.Lukasz     {crow::HTTPMethod::HEAD, {{"Login"}}},
34*43a095abSBorawski.Lukasz     {crow::HTTPMethod::PATCH, {{"ConfigureManager"}}},
35*43a095abSBorawski.Lukasz     {crow::HTTPMethod::PUT, {{"ConfigureManager"}}},
36*43a095abSBorawski.Lukasz     {crow::HTTPMethod::DELETE, {{"ConfigureManager"}}},
37*43a095abSBorawski.Lukasz     {crow::HTTPMethod::POST, {{}}}};
38*43a095abSBorawski.Lukasz 
392b7981f6SKowalski, Kamil class SessionCollection;
402b7981f6SKowalski, Kamil 
412b7981f6SKowalski, Kamil class Sessions : public Node {
422b7981f6SKowalski, Kamil  public:
43*43a095abSBorawski.Lukasz   template <typename CrowApp>
44*43a095abSBorawski.Lukasz   Sessions(CrowApp& app)
45*43a095abSBorawski.Lukasz       : Node(app, EntityPrivileges(std::move(sessionOpMap)),
462b7981f6SKowalski, Kamil              "/redfish/v1/SessionService/Sessions/<str>", std::string()) {
47aecb47a4SBorawski.Lukasz     nodeJson["@odata.type"] = "#Session.v1_0_2.Session";
482b7981f6SKowalski, Kamil     nodeJson["@odata.context"] = "/redfish/v1/$metadata#Session.Session";
492b7981f6SKowalski, Kamil     nodeJson["Name"] = "User Session";
502b7981f6SKowalski, Kamil     nodeJson["Description"] = "Manager User Session";
512b7981f6SKowalski, Kamil   }
522b7981f6SKowalski, Kamil 
532b7981f6SKowalski, Kamil  private:
542b7981f6SKowalski, Kamil   void doGet(crow::response& res, const crow::request& req,
552b7981f6SKowalski, Kamil              const std::vector<std::string>& params) override {
562b7981f6SKowalski, Kamil     auto session =
572b7981f6SKowalski, Kamil         crow::PersistentData::session_store->get_session_by_uid(params[0]);
582b7981f6SKowalski, Kamil 
592b7981f6SKowalski, Kamil     if (session == nullptr) {
602b7981f6SKowalski, Kamil       res.code = static_cast<int>(HttpRespCode::NOT_FOUND);
612b7981f6SKowalski, Kamil       res.end();
622b7981f6SKowalski, Kamil       return;
632b7981f6SKowalski, Kamil     }
642b7981f6SKowalski, Kamil 
652b7981f6SKowalski, Kamil     nodeJson["Id"] = session->unique_id;
662b7981f6SKowalski, Kamil     nodeJson["UserName"] = session->username;
672b7981f6SKowalski, Kamil     nodeJson["@odata.id"] =
682b7981f6SKowalski, Kamil         "/redfish/v1/SessionService/Sessions/" + session->unique_id;
692b7981f6SKowalski, Kamil 
702b7981f6SKowalski, Kamil     res.json_value = nodeJson;
712b7981f6SKowalski, Kamil     res.end();
722b7981f6SKowalski, Kamil   }
732b7981f6SKowalski, Kamil 
742b7981f6SKowalski, Kamil   void doDelete(crow::response& res, const crow::request& req,
752b7981f6SKowalski, Kamil                 const std::vector<std::string>& params) override {
762b7981f6SKowalski, Kamil     // Need only 1 param which should be id of session to be deleted
772b7981f6SKowalski, Kamil     if (params.size() != 1) {
782b7981f6SKowalski, Kamil       res.code = static_cast<int>(HttpRespCode::BAD_REQUEST);
792b7981f6SKowalski, Kamil       res.end();
802b7981f6SKowalski, Kamil       return;
812b7981f6SKowalski, Kamil     }
822b7981f6SKowalski, Kamil 
832b7981f6SKowalski, Kamil     auto session =
842b7981f6SKowalski, Kamil         crow::PersistentData::session_store->get_session_by_uid(params[0]);
852b7981f6SKowalski, Kamil 
862b7981f6SKowalski, Kamil     if (session == nullptr) {
872b7981f6SKowalski, Kamil       res.code = static_cast<int>(HttpRespCode::NOT_FOUND);
882b7981f6SKowalski, Kamil       res.end();
892b7981f6SKowalski, Kamil       return;
902b7981f6SKowalski, Kamil     }
912b7981f6SKowalski, Kamil 
922b7981f6SKowalski, Kamil     crow::PersistentData::session_store->remove_session(session);
932b7981f6SKowalski, Kamil     res.code = static_cast<int>(HttpRespCode::OK);
942b7981f6SKowalski, Kamil     res.end();
952b7981f6SKowalski, Kamil   }
962b7981f6SKowalski, Kamil 
972b7981f6SKowalski, Kamil   /**
982b7981f6SKowalski, Kamil    * This allows SessionCollection to reuse this class' doGet method, to
992b7981f6SKowalski, Kamil    * maintain consistency of returned data, as Collection's doPost should return
1002b7981f6SKowalski, Kamil    * data for created member which should match member's doGet result in 100%
1012b7981f6SKowalski, Kamil    */
1022b7981f6SKowalski, Kamil   friend SessionCollection;
1032b7981f6SKowalski, Kamil 
1042b7981f6SKowalski, Kamil   nlohmann::json nodeJson;
1052b7981f6SKowalski, Kamil };
1062b7981f6SKowalski, Kamil 
1072b7981f6SKowalski, Kamil class SessionCollection : public Node {
1082b7981f6SKowalski, Kamil  public:
109*43a095abSBorawski.Lukasz   template <typename CrowApp>
110*43a095abSBorawski.Lukasz   SessionCollection(CrowApp& app)
111*43a095abSBorawski.Lukasz       : Node(app, EntityPrivileges(std::move(sessionCollectionOpMap)),
1122b7981f6SKowalski, Kamil              "/redfish/v1/SessionService/Sessions/"),
113*43a095abSBorawski.Lukasz         memberSession(app) {
114aecb47a4SBorawski.Lukasz     nodeJson["@odata.type"] = "#SessionCollection.SessionCollection";
115aecb47a4SBorawski.Lukasz     nodeJson["@odata.id"] = "/redfish/v1/SessionService/Sessions/";
1162b7981f6SKowalski, Kamil     nodeJson["@odata.context"] =
1172b7981f6SKowalski, Kamil         "/redfish/v1/$metadata#SessionCollection.SessionCollection";
1182b7981f6SKowalski, Kamil     nodeJson["Name"] = "Session Collection";
1192b7981f6SKowalski, Kamil     nodeJson["Description"] = "Session Collection";
1202b7981f6SKowalski, Kamil     nodeJson["Members@odata.count"] = 0;
1212b7981f6SKowalski, Kamil     nodeJson["Members"] = nlohmann::json::array();
1222b7981f6SKowalski, Kamil   }
1232b7981f6SKowalski, Kamil 
1242b7981f6SKowalski, Kamil  private:
1252b7981f6SKowalski, Kamil   void doGet(crow::response& res, const crow::request& req,
1262b7981f6SKowalski, Kamil              const std::vector<std::string>& params) override {
1272b7981f6SKowalski, Kamil     std::vector<const std::string*> session_ids =
1282b7981f6SKowalski, Kamil         crow::PersistentData::session_store->get_unique_ids(
1292b7981f6SKowalski, Kamil             false, crow::PersistentData::PersistenceType::TIMEOUT);
1302b7981f6SKowalski, Kamil 
1312b7981f6SKowalski, Kamil     nodeJson["Members@odata.count"] = session_ids.size();
1322b7981f6SKowalski, Kamil     nodeJson["Members"] = nlohmann::json::array();
1332b7981f6SKowalski, Kamil     for (const auto& uid : session_ids) {
1342b7981f6SKowalski, Kamil       nodeJson["Members"].push_back(
1352b7981f6SKowalski, Kamil           {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}});
1362b7981f6SKowalski, Kamil     }
1372b7981f6SKowalski, Kamil 
1382b7981f6SKowalski, Kamil     res.json_value = nodeJson;
1392b7981f6SKowalski, Kamil     res.end();
1402b7981f6SKowalski, Kamil   }
1412b7981f6SKowalski, Kamil 
1422b7981f6SKowalski, Kamil   void doPost(crow::response& res, const crow::request& req,
1432b7981f6SKowalski, Kamil               const std::vector<std::string>& params) override {
1442b7981f6SKowalski, Kamil     std::string username;
1452b7981f6SKowalski, Kamil     bool userAuthSuccessful = authenticateUser(req, &res.code, &username);
1462b7981f6SKowalski, Kamil     if (!userAuthSuccessful) {
1472b7981f6SKowalski, Kamil       res.end();
1482b7981f6SKowalski, Kamil       return;
1492b7981f6SKowalski, Kamil     }
1502b7981f6SKowalski, Kamil 
1512b7981f6SKowalski, Kamil     // User is authenticated - create session for him
1522b7981f6SKowalski, Kamil     auto session =
1532b7981f6SKowalski, Kamil         crow::PersistentData::session_store->generate_user_session(username);
1542b7981f6SKowalski, Kamil     res.add_header("X-Auth-Token", session.session_token);
1552b7981f6SKowalski, Kamil 
1562b7981f6SKowalski, Kamil     // Return data for created session
1572b7981f6SKowalski, Kamil     memberSession.doGet(res, req, {session.unique_id});
1582b7981f6SKowalski, Kamil 
1592b7981f6SKowalski, Kamil     // No need for res.end(), as it is called by doGet()
1602b7981f6SKowalski, Kamil   }
1612b7981f6SKowalski, Kamil 
1622b7981f6SKowalski, Kamil   /**
1632b7981f6SKowalski, Kamil    * @brief Verifies data provided in request and tries to authenticate user
1642b7981f6SKowalski, Kamil    *
1652b7981f6SKowalski, Kamil    * @param[in]  req            Crow request containing authentication data
1662b7981f6SKowalski, Kamil    * @param[out] httpRespCode   HTTP Code that should be returned in response
1672b7981f6SKowalski, Kamil    * @param[out] user           Retrieved username - not filled on failure
1682b7981f6SKowalski, Kamil    *
1692b7981f6SKowalski, Kamil    * @return true if authentication was successful, false otherwise
1702b7981f6SKowalski, Kamil    */
1712b7981f6SKowalski, Kamil   bool authenticateUser(const crow::request& req, int* httpRespCode,
1722b7981f6SKowalski, Kamil                         std::string* user) {
1732b7981f6SKowalski, Kamil     // We need only UserName and Password - nothing more, nothing less
1742b7981f6SKowalski, Kamil     static constexpr const unsigned int numberOfRequiredFieldsInReq = 2;
1752b7981f6SKowalski, Kamil 
1762b7981f6SKowalski, Kamil     // call with exceptions disabled
1772b7981f6SKowalski, Kamil     auto login_credentials = nlohmann::json::parse(req.body, nullptr, false);
1782b7981f6SKowalski, Kamil     if (login_credentials.is_discarded()) {
1792b7981f6SKowalski, Kamil       *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST);
1802b7981f6SKowalski, Kamil 
1812b7981f6SKowalski, Kamil       return false;
1822b7981f6SKowalski, Kamil     }
1832b7981f6SKowalski, Kamil 
1842b7981f6SKowalski, Kamil     // Check that there are only as many fields as there should be
1852b7981f6SKowalski, Kamil     if (login_credentials.size() != numberOfRequiredFieldsInReq) {
1862b7981f6SKowalski, Kamil       *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST);
1872b7981f6SKowalski, Kamil 
1882b7981f6SKowalski, Kamil       return false;
1892b7981f6SKowalski, Kamil     }
1902b7981f6SKowalski, Kamil 
1912b7981f6SKowalski, Kamil     // Find fields that we need - UserName and Password
1922b7981f6SKowalski, Kamil     auto user_it = login_credentials.find("UserName");
1932b7981f6SKowalski, Kamil     auto pass_it = login_credentials.find("Password");
1942b7981f6SKowalski, Kamil     if (user_it == login_credentials.end() ||
1952b7981f6SKowalski, Kamil         pass_it == login_credentials.end()) {
1962b7981f6SKowalski, Kamil       *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST);
1972b7981f6SKowalski, Kamil 
1982b7981f6SKowalski, Kamil       return false;
1992b7981f6SKowalski, Kamil     }
2002b7981f6SKowalski, Kamil 
2012b7981f6SKowalski, Kamil     // Check that given data is of valid type (string)
2022b7981f6SKowalski, Kamil     if (!user_it->is_string() || !pass_it->is_string()) {
2032b7981f6SKowalski, Kamil       *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST);
2042b7981f6SKowalski, Kamil 
2052b7981f6SKowalski, Kamil       return false;
2062b7981f6SKowalski, Kamil     }
2072b7981f6SKowalski, Kamil 
2082b7981f6SKowalski, Kamil     // Extract username and password
2092b7981f6SKowalski, Kamil     std::string username = user_it->get<const std::string>();
2102b7981f6SKowalski, Kamil     std::string password = pass_it->get<const std::string>();
2112b7981f6SKowalski, Kamil 
2122b7981f6SKowalski, Kamil     // Verify that required fields are not empty
2132b7981f6SKowalski, Kamil     if (username.empty() || password.empty()) {
2142b7981f6SKowalski, Kamil       *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST);
2152b7981f6SKowalski, Kamil 
2162b7981f6SKowalski, Kamil       return false;
2172b7981f6SKowalski, Kamil     }
2182b7981f6SKowalski, Kamil 
2192b7981f6SKowalski, Kamil     // Finally - try to authenticate user
2202b7981f6SKowalski, Kamil     if (!pam_authenticate_user(username, password)) {
2212b7981f6SKowalski, Kamil       *httpRespCode = static_cast<int>(HttpRespCode::UNAUTHORIZED);
2222b7981f6SKowalski, Kamil 
2232b7981f6SKowalski, Kamil       return false;
2242b7981f6SKowalski, Kamil     }
2252b7981f6SKowalski, Kamil 
2262b7981f6SKowalski, Kamil     // User authenticated successfully
2272b7981f6SKowalski, Kamil     *httpRespCode = static_cast<int>(HttpRespCode::OK);
2282b7981f6SKowalski, Kamil     *user = username;
2292b7981f6SKowalski, Kamil 
2302b7981f6SKowalski, Kamil     return true;
2312b7981f6SKowalski, Kamil   }
2322b7981f6SKowalski, Kamil 
2332b7981f6SKowalski, Kamil   /**
2342b7981f6SKowalski, Kamil    * Member session to ensure consistency between collection's doPost and
2352b7981f6SKowalski, Kamil    * member's doGet, as they should return 100% matching data
2362b7981f6SKowalski, Kamil    */
2372b7981f6SKowalski, Kamil   Sessions memberSession;
2382b7981f6SKowalski, Kamil   nlohmann::json nodeJson;
2392b7981f6SKowalski, Kamil };
2402b7981f6SKowalski, Kamil 
2412b7981f6SKowalski, Kamil }  // namespace redfish
242