blob: 75857a152850182fc7afc43666f24b471c82d299 [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
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010039class SessionCollection;
40
41class Sessions : public Node {
42 public:
Borawski.Lukasz43a095a2018-02-19 15:39:01 +010043 Sessions(CrowApp& app)
44 : Node(app, EntityPrivileges(std::move(sessionOpMap)),
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010045 "/redfish/v1/SessionService/Sessions/<str>", std::string()) {
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +010046 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";
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010050 }
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
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +010064 Node::json["Id"] = session->unique_id;
65 Node::json["UserName"] = session->username;
66 Node::json["@odata.id"] =
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010067 "/redfish/v1/SessionService/Sessions/" + session->unique_id;
68
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +010069 res.json_value = Node::json;
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010070 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;
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100102};
103
104class SessionCollection : public Node {
105 public:
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100106 SessionCollection(CrowApp& app)
107 : Node(app, EntityPrivileges(std::move(sessionCollectionOpMap)),
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100108 "/redfish/v1/SessionService/Sessions/"),
Borawski.Lukasz43a095a2018-02-19 15:39:01 +0100109 memberSession(app) {
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +0100110 Node::json["@odata.type"] = "#SessionCollection.SessionCollection";
111 Node::json["@odata.id"] = "/redfish/v1/SessionService/Sessions/";
112 Node::json["@odata.context"] =
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100113 "/redfish/v1/$metadata#SessionCollection.SessionCollection";
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +0100114 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();
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100118 }
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
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +0100127 Node::json["Members@odata.count"] = session_ids.size();
128 Node::json["Members"] = nlohmann::json::array();
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100129 for (const auto& uid : session_ids) {
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +0100130 Node::json["Members"].push_back(
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100131 {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}});
132 }
133
Borawski.Lukaszc1a46bd2018-02-08 13:31:59 +0100134 res.json_value = Node::json;
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100135 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);
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100142 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;
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100234};
235
236} // namespace redfish