blob: 4225cc1896c5150469f426a4445b916bd385165d [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
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010017
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010018#include "node.hpp"
19#include "session_storage_singleton.hpp"
20
21namespace redfish {
22
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010023static 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
31static 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
Borawski.Lukasz5d27b852018-02-08 13:24:24 +010039static OperationMap sessionServiceOpMap = {
40 {crow::HTTPMethod::GET, {{"Login"}}},
41 {crow::HTTPMethod::HEAD, {{"Login"}}},
42 {crow::HTTPMethod::PATCH, {{"ConfigureManager"}}},
43 {crow::HTTPMethod::PUT, {{"ConfigureManager"}}},
44 {crow::HTTPMethod::DELETE, {{"ConfigureManager"}}},
45 {crow::HTTPMethod::POST, {{"ConfigureManager"}}}};
46
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010047class SessionCollection;
48
49class Sessions : public Node {
50 public:
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010051 Sessions(CrowApp& app)
52 : Node(app, EntityPrivileges(std::move(sessionOpMap)),
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010053 "/redfish/v1/SessionService/Sessions/<str>", std::string()) {
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +010054 Node::json["@odata.type"] = "#Session.v1_0_2.Session";
55 Node::json["@odata.context"] = "/redfish/v1/$metadata#Session.Session";
56 Node::json["Name"] = "User Session";
57 Node::json["Description"] = "Manager User Session";
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010058 }
59
60 private:
61 void doGet(crow::response& res, const crow::request& req,
62 const std::vector<std::string>& params) override {
63 auto session =
64 crow::PersistentData::session_store->get_session_by_uid(params[0]);
65
66 if (session == nullptr) {
67 res.code = static_cast<int>(HttpRespCode::NOT_FOUND);
68 res.end();
69 return;
70 }
71
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +010072 Node::json["Id"] = session->unique_id;
73 Node::json["UserName"] = session->username;
74 Node::json["@odata.id"] =
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010075 "/redfish/v1/SessionService/Sessions/" + session->unique_id;
76
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +010077 res.json_value = Node::json;
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010078 res.end();
79 }
80
81 void doDelete(crow::response& res, const crow::request& req,
82 const std::vector<std::string>& params) override {
83 // Need only 1 param which should be id of session to be deleted
84 if (params.size() != 1) {
85 res.code = static_cast<int>(HttpRespCode::BAD_REQUEST);
86 res.end();
87 return;
88 }
89
90 auto session =
91 crow::PersistentData::session_store->get_session_by_uid(params[0]);
92
93 if (session == nullptr) {
94 res.code = static_cast<int>(HttpRespCode::NOT_FOUND);
95 res.end();
96 return;
97 }
98
99 crow::PersistentData::session_store->remove_session(session);
100 res.code = static_cast<int>(HttpRespCode::OK);
101 res.end();
102 }
103
104 /**
105 * This allows SessionCollection to reuse this class' doGet method, to
106 * maintain consistency of returned data, as Collection's doPost should return
107 * data for created member which should match member's doGet result in 100%
108 */
109 friend SessionCollection;
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100110};
111
112class SessionCollection : public Node {
113 public:
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100114 SessionCollection(CrowApp& app)
115 : Node(app, EntityPrivileges(std::move(sessionCollectionOpMap)),
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100116 "/redfish/v1/SessionService/Sessions/"),
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100117 memberSession(app) {
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +0100118 Node::json["@odata.type"] = "#SessionCollection.SessionCollection";
119 Node::json["@odata.id"] = "/redfish/v1/SessionService/Sessions/";
120 Node::json["@odata.context"] =
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100121 "/redfish/v1/$metadata#SessionCollection.SessionCollection";
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +0100122 Node::json["Name"] = "Session Collection";
123 Node::json["Description"] = "Session Collection";
124 Node::json["Members@odata.count"] = 0;
125 Node::json["Members"] = nlohmann::json::array();
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100126 }
127
128 private:
129 void doGet(crow::response& res, const crow::request& req,
130 const std::vector<std::string>& params) override {
131 std::vector<const std::string*> session_ids =
132 crow::PersistentData::session_store->get_unique_ids(
133 false, crow::PersistentData::PersistenceType::TIMEOUT);
134
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +0100135 Node::json["Members@odata.count"] = session_ids.size();
136 Node::json["Members"] = nlohmann::json::array();
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100137 for (const auto& uid : session_ids) {
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +0100138 Node::json["Members"].push_back(
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100139 {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}});
140 }
141
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +0100142 res.json_value = Node::json;
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100143 res.end();
144 }
145
146 void doPost(crow::response& res, const crow::request& req,
147 const std::vector<std::string>& params) override {
148 std::string username;
149 bool userAuthSuccessful = authenticateUser(req, &res.code, &username);
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100150 if (!userAuthSuccessful) {
151 res.end();
152 return;
153 }
154
155 // User is authenticated - create session for him
156 auto session =
157 crow::PersistentData::session_store->generate_user_session(username);
158 res.add_header("X-Auth-Token", session.session_token);
159
160 // Return data for created session
161 memberSession.doGet(res, req, {session.unique_id});
162
163 // No need for res.end(), as it is called by doGet()
164 }
165
166 /**
167 * @brief Verifies data provided in request and tries to authenticate user
168 *
169 * @param[in] req Crow request containing authentication data
170 * @param[out] httpRespCode HTTP Code that should be returned in response
171 * @param[out] user Retrieved username - not filled on failure
172 *
173 * @return true if authentication was successful, false otherwise
174 */
175 bool authenticateUser(const crow::request& req, int* httpRespCode,
176 std::string* user) {
177 // We need only UserName and Password - nothing more, nothing less
178 static constexpr const unsigned int numberOfRequiredFieldsInReq = 2;
179
180 // call with exceptions disabled
181 auto login_credentials = nlohmann::json::parse(req.body, nullptr, false);
182 if (login_credentials.is_discarded()) {
183 *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST);
184
185 return false;
186 }
187
188 // Check that there are only as many fields as there should be
189 if (login_credentials.size() != numberOfRequiredFieldsInReq) {
190 *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST);
191
192 return false;
193 }
194
195 // Find fields that we need - UserName and Password
196 auto user_it = login_credentials.find("UserName");
197 auto pass_it = login_credentials.find("Password");
198 if (user_it == login_credentials.end() ||
199 pass_it == login_credentials.end()) {
200 *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST);
201
202 return false;
203 }
204
205 // Check that given data is of valid type (string)
206 if (!user_it->is_string() || !pass_it->is_string()) {
207 *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST);
208
209 return false;
210 }
211
212 // Extract username and password
213 std::string username = user_it->get<const std::string>();
214 std::string password = pass_it->get<const std::string>();
215
216 // Verify that required fields are not empty
217 if (username.empty() || password.empty()) {
218 *httpRespCode = static_cast<int>(HttpRespCode::BAD_REQUEST);
219
220 return false;
221 }
222
223 // Finally - try to authenticate user
224 if (!pam_authenticate_user(username, password)) {
225 *httpRespCode = static_cast<int>(HttpRespCode::UNAUTHORIZED);
226
227 return false;
228 }
229
230 // User authenticated successfully
231 *httpRespCode = static_cast<int>(HttpRespCode::OK);
232 *user = username;
233
234 return true;
235 }
236
237 /**
238 * Member session to ensure consistency between collection's doPost and
239 * member's doGet, as they should return 100% matching data
240 */
241 Sessions memberSession;
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100242};
243
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100244class SessionService : public Node {
245 public:
246 SessionService(CrowApp& app)
247 : Node(app, EntityPrivileges(std::move(sessionServiceOpMap)),
248 "/redfish/v1/SessionService/") {
249 Node::json["@odata.type"] = "#SessionService.v1_0_2.SessionService";
250 Node::json["@odata.id"] = "/redfish/v1/SessionService/";
251 Node::json["@odata.context"] =
252 "/redfish/v1/$metadata#SessionService.SessionService";
253 Node::json["Name"] = "Session Service";
254 Node::json["Description"] = "Session Service";
255 Node::json["SessionTimeout"] =
256 crow::PersistentData::session_store->get_timeout_in_seconds();
257 Node::json["Status"]["State"] = "Enabled";
258 Node::json["Status"]["Health"] = "OK";
259 Node::json["Status"]["HealthRollup"] = "OK";
260 Node::json["ServiceEnabled"] = true;
261 }
262
263 private:
264 void doGet(crow::response& res, const crow::request& req,
265 const std::vector<std::string>& params) override {
266 res.json_value = Node::json;
267 res.end();
268 }
269};
270
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100271} // namespace redfish