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