blob: fcc86386fae1a920e9ce6df9f811e4da77040538 [file] [log] [blame]
Ed Tanousf9273472017-02-28 16:05:13 -08001#pragma once
2
Ed Tanous911ac312017-08-15 09:37:42 -07003#include <pam_authenticate.hpp>
Ed Tanousba9f9a62017-10-11 16:40:35 -07004#include <persistent_data_middleware.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -07005#include <webassets.hpp>
6#include <random>
7#include <crow/app.h>
Ed Tanouse0d918b2018-03-27 17:41:04 -07008#include <crow/common.h>
Ed Tanousf9273472017-02-28 16:05:13 -08009#include <crow/http_request.h>
10#include <crow/http_response.h>
Ed Tanous4758d5b2017-06-06 15:28:13 -070011#include <boost/container/flat_set.hpp>
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010012
Ed Tanous99923322017-03-03 14:21:24 -080013namespace crow {
Ed Tanousb4d29f42017-03-24 16:39:25 -070014
Ed Tanous55c7b7a2018-05-22 15:27:24 -070015namespace token_authorization {
Ed Tanousb4d29f42017-03-24 16:39:25 -070016
Ed Tanous911ac312017-08-15 09:37:42 -070017class Middleware {
Ed Tanous3dac7492017-08-02 13:46:20 -070018 public:
Ed Tanous55c7b7a2018-05-22 15:27:24 -070019 struct Context {
20 std::shared_ptr<crow::persistent_data::UserSession> session;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010021 };
Kowalski, Kamil2b7981f2018-01-31 13:24:59 +010022
Ed Tanous55c7b7a2018-05-22 15:27:24 -070023 void beforeHandle(crow::Request& req, Response& res, Context& ctx) {
24 if (isOnWhitelist(req)) {
Ed Tanousbae064e2018-03-22 15:44:39 -070025 return;
26 }
27
Ed Tanous55c7b7a2018-05-22 15:27:24 -070028 ctx.session = performXtokenAuth(req);
Ed Tanous1ea9f062018-03-27 17:45:20 -070029 if (ctx.session == nullptr) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070030 ctx.session = performCookieAuth(req);
Ed Tanous1ea9f062018-03-27 17:45:20 -070031 }
Ed Tanouse0d918b2018-03-27 17:41:04 -070032 if (ctx.session == nullptr) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070033 boost::string_view authHeader = req.getHeaderValue("Authorization");
34 if (!authHeader.empty()) {
Ed Tanouse0d918b2018-03-27 17:41:04 -070035 // Reject any kind of auth other than basic or token
Ed Tanous55c7b7a2018-05-22 15:27:24 -070036 if (boost::starts_with(authHeader, "Token ")) {
37 ctx.session = performTokenAuth(authHeader);
38 } else if (boost::starts_with(authHeader, "Basic ")) {
39 ctx.session = performBasicAuth(authHeader);
Ed Tanouse0d918b2018-03-27 17:41:04 -070040 }
41 }
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010042 }
43
Ed Tanousbae064e2018-03-22 15:44:39 -070044 if (ctx.session == nullptr) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070045 BMCWEB_LOG_WARNING << "[AuthMiddleware] authorization failed";
Ed Tanouse0d918b2018-03-27 17:41:04 -070046
Ed Tanous9bd21fc2018-04-26 16:08:56 -070047 // If it's a browser connecting, don't send the HTTP authenticate header,
48 // to avoid possible CSRF attacks with basic auth
Ed Tanous55c7b7a2018-05-22 15:27:24 -070049 if (http_helpers::requestPrefersHtml(req)) {
Ed Tanous9bd21fc2018-04-26 16:08:56 -070050 res.result(boost::beast::http::status::temporary_redirect);
Ed Tanous55c7b7a2018-05-22 15:27:24 -070051 res.addHeader("Location", "/#/login");
Ed Tanous9bd21fc2018-04-26 16:08:56 -070052 } else {
53 res.result(boost::beast::http::status::unauthorized);
54 // only send the WWW-authenticate header if this isn't a xhr from the
55 // browser. most scripts,
Ed Tanous55c7b7a2018-05-22 15:27:24 -070056 if (req.getHeaderValue("User-Agent").empty()) {
57 res.addHeader("WWW-Authenticate", "Basic");
Ed Tanous9bd21fc2018-04-26 16:08:56 -070058 }
59 }
Ed Tanouse0d918b2018-03-27 17:41:04 -070060
Ed Tanousf3d847c2017-06-12 16:01:42 -070061 res.end();
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010062 return;
63 }
Ed Tanousf9273472017-02-28 16:05:13 -080064
Ed Tanous55c7b7a2018-05-22 15:27:24 -070065 // TODO get user privileges here and propagate it via MW Context
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010066 // else let the request continue unharmed
67 }
Ed Tanousf3d847c2017-06-12 16:01:42 -070068
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010069 template <typename AllContext>
Ed Tanous55c7b7a2018-05-22 15:27:24 -070070 void afterHandle(Request& req, Response& res, Context& ctx,
71 AllContext& allctx) {
Ed Tanouse0d918b2018-03-27 17:41:04 -070072 // TODO(ed) THis should really be handled by the persistent data
73 // middleware, but because it is upstream, it doesn't have access to the
74 // session information. Should the data middleware persist the current
75 // user session?
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010076 if (ctx.session != nullptr &&
77 ctx.session->persistence ==
Ed Tanous55c7b7a2018-05-22 15:27:24 -070078 crow::persistent_data::PersistenceType::SINGLE_REQUEST) {
79 persistent_data::SessionStore::getInstance().removeSession(ctx.session);
Ed Tanousf3d847c2017-06-12 16:01:42 -070080 }
81 }
82
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010083 private:
Ed Tanous55c7b7a2018-05-22 15:27:24 -070084 const std::shared_ptr<crow::persistent_data::UserSession> performBasicAuth(
Ed Tanouse0d918b2018-03-27 17:41:04 -070085 boost::string_view auth_header) const {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070086 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Basic authentication";
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010087
Ed Tanous55c7b7a2018-05-22 15:27:24 -070088 std::string authData;
Ed Tanouse0d918b2018-03-27 17:41:04 -070089 boost::string_view param = auth_header.substr(strlen("Basic "));
Ed Tanous55c7b7a2018-05-22 15:27:24 -070090 if (!crow::utility::base64Decode(param, authData)) {
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010091 return nullptr;
92 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070093 std::size_t separator = authData.find(':');
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010094 if (separator == std::string::npos) {
95 return nullptr;
96 }
97
Ed Tanous55c7b7a2018-05-22 15:27:24 -070098 std::string user = authData.substr(0, separator);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010099 separator += 1;
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700100 if (separator > authData.size()) {
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100101 return nullptr;
102 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700103 std::string pass = authData.substr(separator);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100104
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700105 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Authenticating user: " << user;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100106
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700107 if (!pamAuthenticateUser(user, pass)) {
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100108 return nullptr;
109 }
110
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700111 // TODO(ed) generateUserSession is a little expensive for basic
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100112 // auth, as it generates some random identifiers that will never be
113 // used. This should have a "fast" path for when user tokens aren't
114 // needed.
115 // This whole flow needs to be revisited anyway, as we can't be
116 // calling directly into pam for every request
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700117 return persistent_data::SessionStore::getInstance().generateUserSession(
118 user, crow::persistent_data::PersistenceType::SINGLE_REQUEST);
Ed Tanousf3d847c2017-06-12 16:01:42 -0700119 }
Ed Tanous8041f312017-04-03 09:47:01 -0700120
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700121 const std::shared_ptr<crow::persistent_data::UserSession> performTokenAuth(
Ed Tanouse0d918b2018-03-27 17:41:04 -0700122 boost::string_view auth_header) const {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700123 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Token authentication";
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100124
Ed Tanouse0d918b2018-03-27 17:41:04 -0700125 boost::string_view token = auth_header.substr(strlen("Token "));
Borawski.Lukasz4b1b8682018-04-04 12:50:16 +0200126 auto session =
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700127 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100128 return session;
129 }
130
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700131 const std::shared_ptr<crow::persistent_data::UserSession> performXtokenAuth(
132 const crow::Request& req) const {
133 BMCWEB_LOG_DEBUG << "[AuthMiddleware] X-Auth-Token authentication";
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100134
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700135 boost::string_view token = req.getHeaderValue("X-Auth-Token");
Ed Tanous1ea9f062018-03-27 17:45:20 -0700136 if (token.empty()) {
137 return nullptr;
138 }
Borawski.Lukasz4b1b8682018-04-04 12:50:16 +0200139 auto session =
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700140 persistent_data::SessionStore::getInstance().loginSessionByToken(token);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100141 return session;
142 }
143
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700144 const std::shared_ptr<crow::persistent_data::UserSession> performCookieAuth(
145 const crow::Request& req) const {
146 BMCWEB_LOG_DEBUG << "[AuthMiddleware] Cookie authentication";
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100147
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700148 boost::string_view cookieValue = req.getHeaderValue("Cookie");
149 if (cookieValue.empty()) {
Ed Tanous1ea9f062018-03-27 17:45:20 -0700150 return nullptr;
151 }
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100152
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700153 auto startIndex = cookieValue.find("SESSION=");
154 if (startIndex == std::string::npos) {
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100155 return nullptr;
156 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700157 startIndex += sizeof("SESSION=") - 1;
158 auto endIndex = cookieValue.find(";", startIndex);
159 if (endIndex == std::string::npos) {
160 endIndex = cookieValue.size();
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100161 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700162 boost::string_view authKey =
163 cookieValue.substr(startIndex, endIndex - startIndex);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100164
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700165 const std::shared_ptr<crow::persistent_data::UserSession> session =
166 persistent_data::SessionStore::getInstance().loginSessionByToken(
167 authKey);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100168 if (session == nullptr) {
169 return nullptr;
170 }
Ed Tanous1e439872018-05-18 11:48:52 -0700171#ifndef BMCWEB_INSECURE_DISABLE_CSRF_PREVENTION
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100172 // RFC7231 defines methods that need csrf protection
Ed Tanouse0d918b2018-03-27 17:41:04 -0700173 if (req.method() != "GET"_method) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700174 boost::string_view csrf = req.getHeaderValue("X-XSRF-TOKEN");
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100175 // Make sure both tokens are filled
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700176 if (csrf.empty() || session->csrfToken.empty()) {
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100177 return nullptr;
178 }
179 // Reject if csrf token not available
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700180 if (csrf != session->csrfToken) {
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100181 return nullptr;
182 }
183 }
Ed Tanous1e439872018-05-18 11:48:52 -0700184#endif
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100185 return session;
186 }
187
188 // checks if request can be forwarded without authentication
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700189 bool isOnWhitelist(const crow::Request& req) const {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700190 // it's allowed to GET root node without authentica tion
191 if ("GET"_method == req.method()) {
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700192 if (req.url == "/redfish/v1" || req.url == "/redfish/v1/") {
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100193 return true;
Ed Tanouse0d918b2018-03-27 17:41:04 -0700194 } else if (crow::webassets::routes.find(std::string(req.url)) !=
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100195 crow::webassets::routes.end()) {
196 return true;
197 }
198 }
199
Ed Tanouse0d918b2018-03-27 17:41:04 -0700200 // it's allowed to POST on session collection & login without
201 // authentication
202 if ("POST"_method == req.method()) {
203 if ((req.url == "/redfish/v1/SessionService/Sessions") ||
204 (req.url == "/redfish/v1/SessionService/Sessions/") ||
Ed Tanous9bd21fc2018-04-26 16:08:56 -0700205 (req.url == "/login")) {
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100206 return true;
207 }
208 }
209
210 return false;
211 }
Ed Tanous99923322017-03-03 14:21:24 -0800212};
Ed Tanousf3d847c2017-06-12 16:01:42 -0700213
Ed Tanousba9f9a62017-10-11 16:40:35 -0700214// TODO(ed) see if there is a better way to allow middlewares to request
215// routes.
Ed Tanous911ac312017-08-15 09:37:42 -0700216// Possibly an init function on first construction?
217template <typename... Middlewares>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700218void requestRoutes(Crow<Middlewares...>& app) {
Ed Tanousba9f9a62017-10-11 16:40:35 -0700219 static_assert(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700220 black_magic::Contains<persistent_data::Middleware, Middlewares...>::value,
221 "token_authorization middleware must be enabled in app to use "
Ed Tanousba9f9a62017-10-11 16:40:35 -0700222 "auth routes");
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700223 BMCWEB_ROUTE(app, "/login")
Ed Tanous911ac312017-08-15 09:37:42 -0700224 .methods(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700225 "POST"_method)([&](const crow::Request& req, crow::Response& res) {
226 boost::string_view contentType = req.getHeaderValue("content-type");
Ed Tanouse0d918b2018-03-27 17:41:04 -0700227 boost::string_view username;
228 boost::string_view password;
229
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700230 bool looksLikeIbm = false;
Ed Tanousdb024a52018-03-06 12:50:34 -0800231
Ed Tanouse0d918b2018-03-27 17:41:04 -0700232 // This object needs to be declared at this scope so the strings
233 // within it are not destroyed before we can use them
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700234 nlohmann::json loginCredentials;
Ed Tanous911ac312017-08-15 09:37:42 -0700235 // Check if auth was provided by a payload
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700236 if (contentType == "application/json") {
237 loginCredentials = nlohmann::json::parse(req.body, nullptr, false);
238 if (loginCredentials.is_discarded()) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700239 res.result(boost::beast::http::status::bad_request);
Ed Tanous911ac312017-08-15 09:37:42 -0700240 res.end();
241 return;
242 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700243
Ed Tanousba9f9a62017-10-11 16:40:35 -0700244 // check for username/password in the root object
245 // THis method is how intel APIs authenticate
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700246 nlohmann::json::iterator userIt = loginCredentials.find("username");
247 nlohmann::json::iterator passIt = loginCredentials.find("password");
248 if (userIt != loginCredentials.end() &&
249 passIt != loginCredentials.end()) {
250 const std::string* userStr = userIt->get_ptr<const std::string*>();
251 const std::string* passStr = passIt->get_ptr<const std::string*>();
252 if (userStr != nullptr && passStr != nullptr) {
253 username = *userStr;
254 password = *passStr;
Ed Tanouse0d918b2018-03-27 17:41:04 -0700255 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700256 } else {
257 // Openbmc appears to push a data object that contains the same
258 // keys (username and password), attempt to use that
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700259 auto dataIt = loginCredentials.find("data");
260 if (dataIt != loginCredentials.end()) {
Ed Tanousba9f9a62017-10-11 16:40:35 -0700261 // Some apis produce an array of value ["username",
262 // "password"]
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700263 if (dataIt->is_array()) {
264 if (dataIt->size() == 2) {
265 nlohmann::json::iterator userIt2 = dataIt->begin();
266 nlohmann::json::iterator passIt2 = dataIt->begin() + 1;
267 looksLikeIbm = true;
268 if (userIt2 != dataIt->end() && passIt2 != dataIt->end()) {
269 const std::string* userStr =
270 userIt2->get_ptr<const std::string*>();
271 const std::string* passStr =
272 passIt2->get_ptr<const std::string*>();
273 if (userStr != nullptr && passStr != nullptr) {
274 username = *userStr;
275 password = *passStr;
Ed Tanouse0d918b2018-03-27 17:41:04 -0700276 }
277 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700278 }
Ed Tanouse0d918b2018-03-27 17:41:04 -0700279
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700280 } else if (dataIt->is_object()) {
281 nlohmann::json::iterator userIt2 = dataIt->find("username");
282 nlohmann::json::iterator passIt2 = dataIt->find("password");
283 if (userIt2 != dataIt->end() && passIt2 != dataIt->end()) {
284 const std::string* userStr =
285 userIt2->get_ptr<const std::string*>();
286 const std::string* passStr =
287 passIt2->get_ptr<const std::string*>();
288 if (userStr != nullptr && passStr != nullptr) {
289 username = *userStr;
290 password = *passStr;
Ed Tanouse0d918b2018-03-27 17:41:04 -0700291 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700292 }
293 }
294 }
295 }
Ed Tanous911ac312017-08-15 09:37:42 -0700296 } else {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700297 // check if auth was provided as a headers
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700298 username = req.getHeaderValue("username");
299 password = req.getHeaderValue("password");
Ed Tanous911ac312017-08-15 09:37:42 -0700300 }
301
Ed Tanouse0d918b2018-03-27 17:41:04 -0700302 if (!username.empty() && !password.empty()) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700303 if (!pamAuthenticateUser(username, password)) {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700304 res.result(boost::beast::http::status::unauthorized);
Ed Tanous911ac312017-08-15 09:37:42 -0700305 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700306 auto session = persistent_data::SessionStore::getInstance()
307 .generateUserSession(username);
Ed Tanous911ac312017-08-15 09:37:42 -0700308
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700309 if (looksLikeIbm) {
Ed Tanousba9f9a62017-10-11 16:40:35 -0700310 // IBM requires a very specific login structure, and doesn't
311 // actually look at the status code. TODO(ed).... Fix that
312 // upstream
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700313 res.jsonValue = {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700314 {"data", "User '" + std::string(username) + "' logged in"},
315 {"message", "200 OK"},
316 {"status", "ok"}};
Ed Tanous9bd21fc2018-04-26 16:08:56 -0700317
318 // Hack alert. Boost beast by default doesn't let you declare
319 // multiple headers of the same name, and in most cases this is
320 // fine. Unfortunately here we need to set the Session cookie,
321 // which requires the httpOnly attribute, as well as the XSRF
322 // cookie, which requires it to not have an httpOnly attribute.
323 // To get the behavior we want, we simply inject the second
324 // "set-cookie" string into the value header, and get the result
325 // we want, even though we are technicaly declaring two headers
326 // here.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700327 res.addHeader("Set-Cookie",
328 "XSRF-TOKEN=" + session->csrfToken +
329 "; Secure\r\nSet-Cookie: SESSION=" +
330 session->sessionToken + "; Secure; HttpOnly");
Ed Tanousba9f9a62017-10-11 16:40:35 -0700331 } else {
332 // if content type is json, assume json token
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700333 res.jsonValue = {{"token", session->sessionToken}};
Ed Tanous911ac312017-08-15 09:37:42 -0700334 }
335 }
336
337 } else {
Ed Tanouse0d918b2018-03-27 17:41:04 -0700338 res.result(boost::beast::http::status::bad_request);
Ed Tanous911ac312017-08-15 09:37:42 -0700339 }
340 res.end();
341 });
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100342
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700343 BMCWEB_ROUTE(app, "/logout")
Borawski.Lukasz4b1b8682018-04-04 12:50:16 +0200344 .methods(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700345 "POST"_method)([&](const crow::Request& req, crow::Response& res) {
Borawski.Lukasz4b1b8682018-04-04 12:50:16 +0200346 auto& session =
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700347 app.template getContext<token_authorization::Middleware>(req)
Borawski.Lukasz4b1b8682018-04-04 12:50:16 +0200348 .session;
349 if (session != nullptr) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700350 persistent_data::SessionStore::getInstance().removeSession(session);
Borawski.Lukasz4b1b8682018-04-04 12:50:16 +0200351 }
352 res.end();
353 return;
354 });
Ed Tanous911ac312017-08-15 09:37:42 -0700355}
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700356} // namespace token_authorization
Ed Tanousba9f9a62017-10-11 16:40:35 -0700357} // namespace crow