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