summaryrefslogtreecommitdiff
path: root/include/token_authorization_middleware.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/token_authorization_middleware.hpp')
-rw-r--r--include/token_authorization_middleware.hpp187
1 files changed, 102 insertions, 85 deletions
diff --git a/include/token_authorization_middleware.hpp b/include/token_authorization_middleware.hpp
index 07540d42b0..49649dd978 100644
--- a/include/token_authorization_middleware.hpp
+++ b/include/token_authorization_middleware.hpp
@@ -5,10 +5,9 @@
#include <webassets.hpp>
#include <random>
#include <crow/app.h>
-#include <crow/http_codes.h>
+#include <crow/common.h>
#include <crow/http_request.h>
#include <crow/http_response.h>
-#include <boost/bimap.hpp>
#include <boost/container/flat_set.hpp>
namespace crow {
@@ -18,7 +17,7 @@ namespace TokenAuthorization {
class Middleware {
public:
struct context {
- const crow::PersistentData::UserSession* session;
+ std::shared_ptr<crow::PersistentData::UserSession> session;
};
void before_handle(crow::request& req, response& res, context& ctx) {
@@ -27,25 +26,27 @@ class Middleware {
}
ctx.session = perform_xtoken_auth(req);
-
if (ctx.session == nullptr) {
ctx.session = perform_cookie_auth(req);
}
-
- const std::string& auth_header = req.get_header_value("Authorization");
- // Reject any kind of auth other than basic or token
- if (ctx.session == nullptr && boost::starts_with(auth_header, "Token ")) {
- ctx.session = perform_token_auth(auth_header);
- }
-
- if (ctx.session == nullptr && boost::starts_with(auth_header, "Basic ")) {
- ctx.session = perform_basic_auth(auth_header);
+ if (ctx.session == nullptr) {
+ boost::string_view auth_header = req.get_header_value("Authorization");
+ if (!auth_header.empty()) {
+ // Reject any kind of auth other than basic or token
+ if (boost::starts_with(auth_header, "Token ")) {
+ ctx.session = perform_token_auth(auth_header);
+ } else if (boost::starts_with(auth_header, "Basic ")) {
+ ctx.session = perform_basic_auth(auth_header);
+ }
+ }
}
if (ctx.session == nullptr) {
CROW_LOG_WARNING << "[AuthMiddleware] authorization failed";
- res.code = static_cast<int>(HttpRespCode::UNAUTHORIZED);
+
+ res.result(boost::beast::http::status::unauthorized);
res.add_header("WWW-Authenticate", "Basic");
+
res.end();
return;
}
@@ -57,10 +58,10 @@ class Middleware {
template <typename AllContext>
void after_handle(request& req, response& res, context& ctx,
AllContext& allctx) {
- // TODO(ed) THis should really be handled by the persistent data middleware,
- // but because it is upstream, it doesn't have access to the session
- // information. Should the data middleware persist the current user
- // session?
+ // TODO(ed) THis should really be handled by the persistent data
+ // middleware, but because it is upstream, it doesn't have access to the
+ // session information. Should the data middleware persist the current
+ // user session?
if (ctx.session != nullptr &&
ctx.session->persistence ==
crow::PersistentData::PersistenceType::SINGLE_REQUEST) {
@@ -69,12 +70,12 @@ class Middleware {
}
private:
- const crow::PersistentData::UserSession* perform_basic_auth(
- const std::string& auth_header) const {
+ const std::shared_ptr<crow::PersistentData::UserSession> perform_basic_auth(
+ boost::string_view auth_header) const {
CROW_LOG_DEBUG << "[AuthMiddleware] Basic authentication";
std::string auth_data;
- std::string param = auth_header.substr(strlen("Basic "));
+ boost::string_view param = auth_header.substr(strlen("Basic "));
if (!crow::utility::base64_decode(param, auth_data)) {
return nullptr;
}
@@ -102,24 +103,24 @@ class Middleware {
// needed.
// This whole flow needs to be revisited anyway, as we can't be
// calling directly into pam for every request
- return &(PersistentData::session_store->generate_user_session(
- user, crow::PersistentData::PersistenceType::SINGLE_REQUEST));
+ return PersistentData::session_store->generate_user_session(
+ user, crow::PersistentData::PersistenceType::SINGLE_REQUEST);
}
- const crow::PersistentData::UserSession* perform_token_auth(
- const std::string& auth_header) const {
+ const std::shared_ptr<crow::PersistentData::UserSession> perform_token_auth(
+ boost::string_view auth_header) const {
CROW_LOG_DEBUG << "[AuthMiddleware] Token authentication";
- std::string token = auth_header.substr(strlen("Token "));
+ boost::string_view token = auth_header.substr(strlen("Token "));
auto session = PersistentData::session_store->login_session_by_token(token);
return session;
}
- const crow::PersistentData::UserSession* perform_xtoken_auth(
+ const std::shared_ptr<crow::PersistentData::UserSession> perform_xtoken_auth(
const crow::request& req) const {
CROW_LOG_DEBUG << "[AuthMiddleware] X-Auth-Token authentication";
- const std::string& token = req.get_header_value("X-Auth-Token");
+ boost::string_view token = req.get_header_value("X-Auth-Token");
if (token.empty()) {
return nullptr;
}
@@ -127,11 +128,11 @@ class Middleware {
return session;
}
- const crow::PersistentData::UserSession* perform_cookie_auth(
+ const std::shared_ptr<crow::PersistentData::UserSession> perform_cookie_auth(
const crow::request& req) const {
CROW_LOG_DEBUG << "[AuthMiddleware] Cookie authentication";
- auto& cookie_value = req.get_header_value("Cookie");
+ boost::string_view cookie_value = req.get_header_value("Cookie");
if (cookie_value.empty()) {
return nullptr;
}
@@ -145,18 +146,18 @@ class Middleware {
if (end_index == std::string::npos) {
end_index = cookie_value.size();
}
- std::string auth_key =
+ boost::string_view auth_key =
cookie_value.substr(start_index, end_index - start_index);
- const crow::PersistentData::UserSession* session =
+ const std::shared_ptr<crow::PersistentData::UserSession> session =
PersistentData::session_store->login_session_by_token(auth_key);
if (session == nullptr) {
return nullptr;
}
// RFC7231 defines methods that need csrf protection
- if (req.method != "GET"_method) {
- const std::string& csrf = req.get_header_value("X-XSRF-TOKEN");
+ if (req.method() != "GET"_method) {
+ boost::string_view csrf = req.get_header_value("X-XSRF-TOKEN");
// Make sure both tokens are filled
if (csrf.empty() || session->csrf_token.empty()) {
return nullptr;
@@ -171,21 +172,21 @@ class Middleware {
// checks if request can be forwarded without authentication
bool is_on_whitelist(const crow::request& req) const {
- // it's allowed to GET root node without authentication
- if ("GET"_method == req.method) {
- CROW_LOG_DEBUG << "TESTING ROUTE " << req.url;
+ // it's allowed to GET root node without authentica tion
+ if ("GET"_method == req.method()) {
if (req.url == "/redfish/v1" || req.url == "/redfish/v1/") {
return true;
- } else if (crow::webassets::routes.find(req.url) !=
+ } else if (crow::webassets::routes.find(std::string(req.url)) !=
crow::webassets::routes.end()) {
return true;
}
}
- // it's allowed to POST on session collection & login without authentication
- if ("POST"_method == req.method) {
- if ((req.url == "/redfish/v1/SessionService/Sessions" ||
- req.url == "/redfish/v1/SessionService/Sessions/") ||
+ // it's allowed to POST on session collection & login without
+ // authentication
+ if ("POST"_method == req.method()) {
+ if ((req.url == "/redfish/v1/SessionService/Sessions") ||
+ (req.url == "/redfish/v1/SessionService/Sessions/") ||
(req.url == "/login") || (req.url == "/logout")) {
return true;
}
@@ -207,35 +208,38 @@ void request_routes(Crow<Middlewares...>& app) {
CROW_ROUTE(app, "/login")
.methods(
"POST"_method)([&](const crow::request& req, crow::response& res) {
- std::string content_type;
- auto content_type_it = req.headers.find("content-type");
- if (content_type_it != req.headers.end()) {
- content_type = content_type_it->second;
- boost::algorithm::to_lower(content_type);
- }
- const std::string* username;
- const std::string* password;
+ boost::string_view content_type = req.get_header_value("content-type");
+ boost::string_view username;
+ boost::string_view password;
+
bool looks_like_ibm = false;
- // This object needs to be declared at this scope so the strings within
- // it are not destroyed before we can use them
+ // This object needs to be declared at this scope so the strings
+ // within it are not destroyed before we can use them
nlohmann::json login_credentials;
// Check if auth was provided by a payload
if (content_type == "application/json") {
login_credentials = nlohmann::json::parse(req.body, nullptr, false);
if (login_credentials.is_discarded()) {
- res.code = 400;
+ res.result(boost::beast::http::status::bad_request);
res.end();
return;
}
+
// check for username/password in the root object
// THis method is how intel APIs authenticate
- auto user_it = login_credentials.find("username");
- auto pass_it = login_credentials.find("password");
+ nlohmann::json::iterator user_it = login_credentials.find("username");
+ nlohmann::json::iterator pass_it = login_credentials.find("password");
if (user_it != login_credentials.end() &&
pass_it != login_credentials.end()) {
- username = user_it->get_ptr<const std::string*>();
- password = pass_it->get_ptr<const std::string*>();
+ const std::string* user_str =
+ user_it->get_ptr<const std::string*>();
+ const std::string* pass_str =
+ pass_it->get_ptr<const std::string*>();
+ if (user_str != nullptr && pass_str != nullptr) {
+ username = *user_str;
+ password = *pass_str;
+ }
} else {
// Openbmc appears to push a data object that contains the same
// keys (username and password), attempt to use that
@@ -245,56 +249,70 @@ void request_routes(Crow<Middlewares...>& app) {
// "password"]
if (data_it->is_array()) {
if (data_it->size() == 2) {
- username = (*data_it)[0].get_ptr<const std::string*>();
- password = (*data_it)[1].get_ptr<const std::string*>();
+ nlohmann::json::iterator user_it2 = data_it->begin();
+ nlohmann::json::iterator pass_it2 = data_it->begin() + 1;
looks_like_ibm = true;
+ if (user_it2 != data_it->end() &&
+ pass_it2 != data_it->end()) {
+ const std::string* user_str =
+ user_it2->get_ptr<const std::string*>();
+ const std::string* pass_str =
+ pass_it2->get_ptr<const std::string*>();
+ if (user_str != nullptr && pass_str != nullptr) {
+ username = *user_str;
+ password = *pass_str;
+ }
+ }
}
+
} else if (data_it->is_object()) {
- auto user_it = data_it->find("username");
- auto pass_it = data_it->find("password");
- if (user_it != data_it->end() && pass_it != data_it->end()) {
- username = user_it->get_ptr<const std::string*>();
- password = pass_it->get_ptr<const std::string*>();
+ nlohmann::json::iterator user_it2 = data_it->find("username");
+ nlohmann::json::iterator pass_it2 = data_it->find("password");
+ if (user_it2 != data_it->end() && pass_it2 != data_it->end()) {
+ const std::string* user_str =
+ user_it2->get_ptr<const std::string*>();
+ const std::string* pass_str =
+ pass_it2->get_ptr<const std::string*>();
+ if (user_str != nullptr && pass_str != nullptr) {
+ username = *user_str;
+ password = *pass_str;
+ }
}
}
}
}
} else {
- // check if auth was provided as a query string
- auto user_it = req.headers.find("username");
- auto pass_it = req.headers.find("password");
- if (user_it != req.headers.end() && pass_it != req.headers.end()) {
- username = &user_it->second;
- password = &pass_it->second;
- }
+ // check if auth was provided as a headers
+ username = req.get_header_value("username");
+ password = req.get_header_value("password");
}
- if (username != nullptr && !username->empty() && password != nullptr &&
- !password->empty()) {
- if (!pam_authenticate_user(*username, *password)) {
- res.code = res.code = static_cast<int>(HttpRespCode::UNAUTHORIZED);
+ if (!username.empty() && !password.empty()) {
+ if (!pam_authenticate_user(username, password)) {
+ res.result(boost::beast::http::status::unauthorized);
} else {
- auto& session =
- PersistentData::session_store->generate_user_session(*username);
+ auto session =
+ PersistentData::session_store->generate_user_session(username);
if (looks_like_ibm) {
// IBM requires a very specific login structure, and doesn't
// actually look at the status code. TODO(ed).... Fix that
// upstream
- res.json_value = {{"data", "User '" + *username + "' logged in"},
- {"message", "200 OK"},
- {"status", "ok"}};
- res.add_header("Set-Cookie", "XSRF-TOKEN=" + session.csrf_token);
- res.add_header("Set-Cookie", "SESSION=" + session.session_token +
+ res.json_value = {
+ {"data", "User '" + std::string(username) + "' logged in"},
+ {"message", "200 OK"},
+ {"status", "ok"}};
+ res.add_header("Set-Cookie", "XSRF-TOKEN=" + session->csrf_token);
+ res.add_header("Set-Cookie", "SESSION=" + session->session_token +
"; Secure; HttpOnly");
} else {
// if content type is json, assume json token
- res.json_value = {{"token", session.session_token}};
+ res.json_value = {{"token", session->session_token}};
}
}
} else {
- res.code = static_cast<int>(HttpRespCode::BAD_REQUEST);
+ res.result(boost::beast::http::status::bad_request);
}
res.end();
});
@@ -308,7 +326,6 @@ void request_routes(Crow<Middlewares...>& app) {
if (session != nullptr) {
PersistentData::session_store->remove_session(session);
}
- res.code = static_cast<int>(HttpRespCode::OK);
res.end();
return;