xref: /openbmc/bmcweb/redfish-core/lib/redfish_sessions.hpp (revision c1a46bd2bd7c242c5b303ca40165cb19002d63e3)
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 class SessionCollection;
40 
41 class Sessions : public Node {
42  public:
43   Sessions(CrowApp& app)
44       : Node(app, EntityPrivileges(std::move(sessionOpMap)),
45              "/redfish/v1/SessionService/Sessions/<str>", std::string()) {
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";
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 
64     Node::json["Id"] = session->unique_id;
65     Node::json["UserName"] = session->username;
66     Node::json["@odata.id"] =
67         "/redfish/v1/SessionService/Sessions/" + session->unique_id;
68 
69     res.json_value = Node::json;
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;
102 };
103 
104 class SessionCollection : public Node {
105  public:
106   SessionCollection(CrowApp& app)
107       : Node(app, EntityPrivileges(std::move(sessionCollectionOpMap)),
108              "/redfish/v1/SessionService/Sessions/"),
109         memberSession(app) {
110     Node::json["@odata.type"] = "#SessionCollection.SessionCollection";
111     Node::json["@odata.id"] = "/redfish/v1/SessionService/Sessions/";
112     Node::json["@odata.context"] =
113         "/redfish/v1/$metadata#SessionCollection.SessionCollection";
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();
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 
127     Node::json["Members@odata.count"] = session_ids.size();
128     Node::json["Members"] = nlohmann::json::array();
129     for (const auto& uid : session_ids) {
130       Node::json["Members"].push_back(
131           {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}});
132     }
133 
134     res.json_value = Node::json;
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);
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;
234 };
235 
236 }  // namespace redfish
237