blob: 4c7a2d427082db935154b495b293d01e3f9021d3 [file] [log] [blame]
#include <boost/algorithm/string/predicate.hpp>
#include <random>
#include <unordered_map>
#include <crow/logging.h>
#include <base64.hpp>
#include <token_authorization_middleware.hpp>
namespace crow {
using random_bytes_engine =
std::independent_bits_engine<std::default_random_engine, CHAR_BIT,
unsigned char>;
TokenAuthorizationMiddleware::TokenAuthorizationMiddleware()
: auth_token2(""){
};
void TokenAuthorizationMiddleware::before_handle(crow::request& req,
response& res, context& ctx) {
auto return_unauthorized = [&req, &res]() {
res.code = 401;
res.end();
};
auto return_bad_request = [&req, &res]() {
res.code = 400;
res.end();
};
LOG(DEBUG) << "Token Auth Got route " << req.url;
if (req.url == "/" || boost::starts_with(req.url, "/static/")) {
// TODO this is total hackery to allow the login page to work before the
// user is authenticated. Also, it will be quite slow for all pages instead
// of a one time hit for the whitelist entries. Ideally, this should be
// done
// in the url router handler, with tagged routes for the whitelist entries.
// Another option would be to whitelist a minimal
return;
}
if (req.url == "/login") {
if (req.method != HTTPMethod::POST) {
return_unauthorized();
return;
} else {
auto login_credentials = crow::json::load(req.body);
if (!login_credentials) {
return_bad_request();
return;
}
if (!login_credentials.has("username") ||
!login_credentials.has("password")) {
return_bad_request();
return;
}
auto username = login_credentials["username"].s();
auto password = login_credentials["password"].s();
// TODO(ed) pull real passwords from PAM
if (username == "dude" && password == "dude") {
// TODO(ed) the RNG should be initialized at start, not every time we
// want a token
std::random_device rand;
random_bytes_engine rbe;
std::string token('a', 20);
// TODO(ed) for some reason clang-tidy finds a divide by zero error in
// cstdlibc here
// commented out for now. Needs investigation
std::generate(begin(token), end(token), std::ref(rbe)); // NOLINT
std::string encoded_token;
base64::base64_encode(token, encoded_token);
ctx.auth_token = encoded_token;
this->auth_token2 = encoded_token;
crow::json::wvalue x;
auto auth_token = ctx.auth_token;
x["token"] = auth_token;
res.write(json::dump(x));
res.add_header("Content-Type", "application/json");
res.end();
} else {
return_unauthorized();
return;
}
}
} else if (req.url == "/logout") {
this->auth_token2 = "";
res.code = 200;
res.end();
} else { // Normal, non login, non static file request
// Check to make sure we're logged in
if (this->auth_token2.empty()) {
return_unauthorized();
return;
}
// Check for an authorization header, reject if not present
if (req.headers.count("Authorization") != 1) {
return_unauthorized();
return;
}
std::string auth_header = req.get_header_value("Authorization");
// If the user is attempting any kind of auth other than token, reject
if (!boost::starts_with(auth_header, "Token ")) {
return_unauthorized();
return;
}
// TODO(ed), use span here instead of constructing a new string
if (auth_header.substr(6) != this->auth_token2) {
return_unauthorized();
return;
}
// else let the request continue unharmed
}
}
void TokenAuthorizationMiddleware::after_handle(request& req, response& res,
context& ctx) {}
}