blob: 11cde7234c4e3b67e8da051329d8a29d9714ea3b [file] [log] [blame]
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +01001/*
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
21namespace redfish {
22
23class SessionCollection;
24
25class 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()) {
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010031 nodeJson["@odata.type"] = "#Session.v1_0_2.Session";
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010032 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
91class 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) {
Borawski.Lukaszaecb47a2018-01-25 12:14:14 +010098 nodeJson["@odata.type"] = "#SessionCollection.SessionCollection";
99 nodeJson["@odata.id"] = "/redfish/v1/SessionService/Sessions/";
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100100 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