server/src/http_auth_request_policy.cpp
Rama main · réplica de lectura del repositorio original.
#include "http_auth_request_policy.h"
#include <boost/algorithm/string.hpp>
#include <filesystem>
#include <format>
#include <nlohmann/json.hpp>
#include <ranges>
namespace confighttp::policy {
AuthResult RequestAuthPolicy::make_error(StatusCode code, const std::string &error) const {
AuthResult result {false, code, {}, {}};
result.headers.emplace("Access-Control-Allow-Origin", std::format("https://localhost:{}", _dependencies.https_port()));
if (!error.empty()) {
result.body = nlohmann::json {{"status", false}, {"error", error}}.dump();
result.headers.emplace("Content-Type", "application/json");
}
return result;
}
AuthResult RequestAuthPolicy::basic_error() const {
auto result = make_error(StatusCode::client_error_unauthorized, "Unauthorized");
result.headers.emplace("WWW-Authenticate", "Basic realm=\"Sunshine\"");
return result;
}
AuthResult RequestAuthPolicy::check_basic(const std::string &raw_auth) const {
const auto space = raw_auth.find(' ');
if (space == std::string::npos || !boost::iequals(raw_auth.substr(0, space), "Basic")) return basic_error();
try {
const auto decoded = _dependencies.decode_base64(raw_auth.substr(space + 1));
const auto colon = decoded.find(':');
if (colon == std::string::npos || !_dependencies.credentials_valid(decoded.substr(0, colon), decoded.substr(colon + 1))) return basic_error();
} catch (...) { return basic_error(); }
return {true, StatusCode::success_ok, {}, {}};
}
AuthResult RequestAuthPolicy::check_bearer(const std::string &raw_auth, const std::string &path, const std::string &method) const {
if (!_dependencies.bearer_valid(raw_auth, path, method)) return make_error(StatusCode::client_error_forbidden, "Forbidden: Token does not have permission for this path/method.");
return {true, StatusCode::success_ok, {}, {}};
}
AuthResult RequestAuthPolicy::check_session(const std::string &raw_auth) const {
if (!raw_auth.starts_with("Session ")) return make_error(StatusCode::client_error_unauthorized, "Invalid session token format");
if (!_dependencies.session_valid(raw_auth.substr(8))) return make_error(StatusCode::client_error_unauthorized, "Invalid or expired session token");
return {true, StatusCode::success_ok, {}, {}};
}
bool RequestAuthPolicy::is_html_request(const std::string &path) const {
if (path.starts_with("/api/") || path.starts_with("/assets/") || path.starts_with("/images/")) return false;
auto ext = boost::algorithm::to_lower_copy(std::filesystem::path(path).extension().string());
static const std::vector<std::string> non_html {".js", ".css", ".map", ".json", ".woff", ".woff2", ".ttf", ".eot", ".ico", ".png", ".jpg", ".jpeg", ".svg"};
return std::ranges::find(non_html, ext) == non_html.end();
}
AuthResult RequestAuthPolicy::check(const std::string &remote_address, const std::string &auth_header, const std::string &path, const std::string &method) const {
auto base_path = path.substr(0, path.find('?'));
if (base_path == "/welcome" || base_path == "/welcome/") return {true, StatusCode::success_ok, {}, {}};
if (!_dependencies.remote_allowed(remote_address)) return make_error(StatusCode::client_error_forbidden, "Forbidden");
const bool is_api = base_path.starts_with("/api/");
if (!is_api || base_path == "/api/auth/login" || base_path == "/api/auth/logout" || base_path == "/api/csrf-token") return {true, StatusCode::success_ok, {}, {}};
if (!_dependencies.credentials_configured()) return make_error(StatusCode::client_error_unauthorized, "Credentials not configured");
if (auth_header.empty()) return make_error(StatusCode::client_error_unauthorized, "Unauthorized");
if (auth_header.starts_with("Bearer ")) return check_bearer(auth_header, path, method);
if (auth_header.starts_with("Session ")) return check_session(auth_header);
if (const auto space = auth_header.find(' '); space != std::string::npos && boost::iequals(auth_header.substr(0, space), "Basic")) return check_basic(auth_header);
return make_error(StatusCode::client_error_unauthorized, "Unauthorized");
}
std::string RequestAuthPolicy::extract_cookie(const SimpleWeb::CaseInsensitiveMultimap &headers, std::string_view name) const {
const auto it = headers.find("Cookie");
if (it == headers.end()) return {};
const auto prefix = std::string(name) + "=";
auto start = it->second.find(prefix);
if (start == std::string::npos) return {};
start += prefix.size();
const auto end = it->second.find(';', start);
return _dependencies.cookie_unescape(it->second.substr(start, end == std::string::npos ? std::string::npos : end - start));
}
} // namespace confighttp::policy