blob: 88f250bc1d9ef90ea5f7e364757681a783af2602 [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, Kamilf4c4dcf2018-01-29 14:55:35 +010018#include "error_messages.hpp"
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010019#include "node.hpp"
Borawski.Lukasz4b1b8682018-04-04 12:50:16 +020020#include "persistent_data_middleware.hpp"
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010021
Ed Tanous1abe55e2018-09-05 08:30:59 -070022namespace redfish
23{
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010024
25class SessionCollection;
26
Ed Tanous1abe55e2018-09-05 08:30:59 -070027class Sessions : public Node
28{
29 public:
30 Sessions(CrowApp& app) :
31 Node(app, "/redfish/v1/SessionService/Sessions/<str>/", std::string())
32 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070033 entityPrivileges = {
34 {boost::beast::http::verb::get, {{"Login"}}},
35 {boost::beast::http::verb::head, {{"Login"}}},
36 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
37 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
Joseph Reynolds900f9492019-11-25 15:37:29 -060038 {boost::beast::http::verb::delete_,
39 {{"ConfigureManager"}, {"ConfigureSelf"}}},
Ed Tanous1abe55e2018-09-05 08:30:59 -070040 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010041 }
42
Ed Tanous1abe55e2018-09-05 08:30:59 -070043 private:
44 void doGet(crow::Response& res, const crow::Request& req,
45 const std::vector<std::string>& params) override
46 {
Joseph Reynolds900f9492019-11-25 15:37:29 -060047 // Note that control also reaches here via doPost and doDelete.
Ed Tanous1abe55e2018-09-05 08:30:59 -070048 auto session =
49 crow::persistent_data::SessionStore::getInstance().getSessionByUid(
50 params[0]);
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010051
Ed Tanous1abe55e2018-09-05 08:30:59 -070052 if (session == nullptr)
53 {
Jason M. Billsf12894f2018-10-09 12:45:45 -070054 messages::resourceNotFound(res, "Session", params[0]);
Ed Tanous1abe55e2018-09-05 08:30:59 -070055 res.end();
56 return;
57 }
Kowalski, Kamilf4c4dcf2018-01-29 14:55:35 +010058
Ed Tanous0f74e642018-11-12 15:17:05 -080059 res.jsonValue["Id"] = session->uniqueId;
60 res.jsonValue["UserName"] = session->username;
61 res.jsonValue["@odata.id"] =
Ed Tanous1abe55e2018-09-05 08:30:59 -070062 "/redfish/v1/SessionService/Sessions/" + session->uniqueId;
Ed Tanous0f74e642018-11-12 15:17:05 -080063 res.jsonValue["@odata.type"] = "#Session.v1_0_2.Session";
64 res.jsonValue["@odata.context"] =
65 "/redfish/v1/$metadata#Session.Session";
66 res.jsonValue["Name"] = "User Session";
67 res.jsonValue["Description"] = "Manager User Session";
Ed Tanouse0d918b2018-03-27 17:41:04 -070068
Ed Tanous1abe55e2018-09-05 08:30:59 -070069 res.end();
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010070 }
71
Ed Tanous1abe55e2018-09-05 08:30:59 -070072 void doDelete(crow::Response& res, const crow::Request& req,
73 const std::vector<std::string>& params) override
74 {
75 // Need only 1 param which should be id of session to be deleted
76 if (params.size() != 1)
77 {
78 // This should be handled by crow and never happen
79 BMCWEB_LOG_ERROR << "Session DELETE has been called with invalid "
80 "number of params";
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010081
Jason M. Billsf12894f2018-10-09 12:45:45 -070082 messages::generalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -070083 res.end();
84 return;
85 }
86
87 auto session =
88 crow::persistent_data::SessionStore::getInstance().getSessionByUid(
89 params[0]);
90
91 if (session == nullptr)
92 {
Jason M. Billsf12894f2018-10-09 12:45:45 -070093 messages::resourceNotFound(res, "Session", params[0]);
Ed Tanous1abe55e2018-09-05 08:30:59 -070094 res.end();
95 return;
96 }
97
Joseph Reynolds900f9492019-11-25 15:37:29 -060098 // Perform a proper ConfigureSelf authority check. If a
99 // session is being used to DELETE some other user's session,
100 // then the ConfigureSelf privilege does not apply. In that
101 // case, perform the authority check again without the user's
102 // ConfigureSelf privilege.
103 if (session->username != req.session->username)
104 {
105 if (!isAllowedWithoutConfigureSelf(req))
106 {
107 BMCWEB_LOG_WARNING << "DELETE Session denied access";
108 messages::insufficientPrivilege(res);
109 res.end();
110 return;
111 }
112 }
113
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 // DELETE should return representation of object that will be removed
115 doGet(res, req, params);
116
117 crow::persistent_data::SessionStore::getInstance().removeSession(
118 session);
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100119 }
120
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 /**
122 * This allows SessionCollection to reuse this class' doGet method, to
123 * maintain consistency of returned data, as Collection's doPost should
124 * return data for created member which should match member's doGet result
125 * in 100%
126 */
127 friend SessionCollection;
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100128};
129
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130class SessionCollection : public Node
131{
132 public:
133 SessionCollection(CrowApp& app) :
134 Node(app, "/redfish/v1/SessionService/Sessions/"), memberSession(app)
135 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700136 entityPrivileges = {
137 {boost::beast::http::verb::get, {{"Login"}}},
138 {boost::beast::http::verb::head, {{"Login"}}},
139 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
140 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
141 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
142 {boost::beast::http::verb::post, {}}};
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100143 }
144
Ed Tanous1abe55e2018-09-05 08:30:59 -0700145 private:
146 void doGet(crow::Response& res, const crow::Request& req,
147 const std::vector<std::string>& params) override
148 {
149 std::vector<const std::string*> sessionIds =
150 crow::persistent_data::SessionStore::getInstance().getUniqueIds(
151 false, crow::persistent_data::PersistenceType::TIMEOUT);
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100152
Ed Tanous0f74e642018-11-12 15:17:05 -0800153 res.jsonValue["Members@odata.count"] = sessionIds.size();
154 res.jsonValue["Members"] = nlohmann::json::array();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700155 for (const std::string* uid : sessionIds)
156 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800157 res.jsonValue["Members"].push_back(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700158 {{"@odata.id", "/redfish/v1/SessionService/Sessions/" + *uid}});
159 }
Tanousf00032d2018-11-05 01:18:10 -0300160 res.jsonValue["Members@odata.count"] = sessionIds.size();
Ed Tanous0f74e642018-11-12 15:17:05 -0800161 res.jsonValue["@odata.type"] = "#SessionCollection.SessionCollection";
162 res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/Sessions/";
163 res.jsonValue["@odata.context"] =
164 "/redfish/v1/$metadata#SessionCollection.SessionCollection";
165 res.jsonValue["Name"] = "Session Collection";
166 res.jsonValue["Description"] = "Session Collection";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700167 res.end();
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100168 }
169
Ed Tanous1abe55e2018-09-05 08:30:59 -0700170 void doPost(crow::Response& res, const crow::Request& req,
171 const std::vector<std::string>& params) override
172 {
Ed Tanous9712f8a2018-09-21 13:38:49 -0700173 std::string username;
174 std::string password;
175 if (!json_util::readJson(req, res, "UserName", username, "Password",
176 password))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700177 {
178 res.end();
179 return;
180 }
Ed Tanousb9845d92018-07-24 14:38:06 -0700181
Ed Tanous820ce592018-10-04 15:54:21 -0700182 if (password.empty() || username.empty() ||
183 res.result() != boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700184 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700185 if (username.empty())
186 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800187 messages::propertyMissing(res, "UserName");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700188 }
189
190 if (password.empty())
191 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800192 messages::propertyMissing(res, "Password");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700193 }
Ed Tanous820ce592018-10-04 15:54:21 -0700194 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700195
Ed Tanous820ce592018-10-04 15:54:21 -0700196 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700197 }
198
Ed Tanous1abe55e2018-09-05 08:30:59 -0700199 if (!pamAuthenticateUser(username, password))
200 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700201 messages::resourceAtUriUnauthorized(res, std::string(req.url),
202 "Invalid username or password");
Ed Tanous820ce592018-10-04 15:54:21 -0700203 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700204
Ed Tanous820ce592018-10-04 15:54:21 -0700205 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700206 }
207
Ed Tanous820ce592018-10-04 15:54:21 -0700208 // User is authenticated - create session
209 std::shared_ptr<crow::persistent_data::UserSession> session =
210 crow::persistent_data::SessionStore::getInstance()
211 .generateUserSession(username);
212 res.addHeader("X-Auth-Token", session->sessionToken);
213 res.addHeader("Location", "/redfish/v1/SessionService/Sessions/" +
214 session->uniqueId);
215 res.result(boost::beast::http::status::created);
216 memberSession.doGet(res, req, {session->uniqueId});
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100217 }
218
Ed Tanous1abe55e2018-09-05 08:30:59 -0700219 /**
220 * Member session to ensure consistency between collection's doPost and
221 * member's doGet, as they should return 100% matching data
222 */
223 Sessions memberSession;
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +0100224};
225
Ed Tanous1abe55e2018-09-05 08:30:59 -0700226class SessionService : public Node
227{
228 public:
229 SessionService(CrowApp& app) : Node(app, "/redfish/v1/SessionService/")
230 {
Ed Tanous3ebd75f2018-03-05 18:20:01 -0800231
Ed Tanous1abe55e2018-09-05 08:30:59 -0700232 entityPrivileges = {
233 {boost::beast::http::verb::get, {{"Login"}}},
234 {boost::beast::http::verb::head, {{"Login"}}},
235 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
236 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
237 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
238 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
239 }
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100240
Ed Tanous1abe55e2018-09-05 08:30:59 -0700241 private:
242 void doGet(crow::Response& res, const crow::Request& req,
243 const std::vector<std::string>& params) override
244 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800245 res.jsonValue["@odata.type"] = "#SessionService.v1_0_2.SessionService";
246 res.jsonValue["@odata.id"] = "/redfish/v1/SessionService/";
247 res.jsonValue["@odata.context"] =
248 "/redfish/v1/$metadata#SessionService.SessionService";
249 res.jsonValue["Name"] = "Session Service";
250 res.jsonValue["Id"] = "SessionService";
251 res.jsonValue["Description"] = "Session Service";
252 res.jsonValue["SessionTimeout"] =
253 crow::persistent_data::SessionStore::getInstance()
254 .getTimeoutInSeconds();
255 res.jsonValue["ServiceEnabled"] = true;
256
Ed Tanous0f261532019-02-08 11:13:29 -0800257 res.jsonValue["Sessions"] = {
258 {"@odata.id", "/redfish/v1/SessionService/Sessions"}};
259
Ed Tanous1abe55e2018-09-05 08:30:59 -0700260 res.end();
261 }
Borawski.Lukasz5d27b852018-02-08 13:24:24 +0100262};
263
Ed Tanous1abe55e2018-09-05 08:30:59 -0700264} // namespace redfish