From 0eacd76810c74064eeae8f38b35f4b79632d8276 Mon Sep 17 00:00:00 2001 From: Vladislav Ostapov Date: Tue, 5 Nov 2024 10:33:52 +0300 Subject: [PATCH] =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D1=8E?= =?UTF-8?q?=D1=89=D0=B0=D1=8F=20=D0=B0=D0=B2=D1=82=D0=BE=D1=80=D0=B8=D0=B7?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/jwt.cpp | 2 +- src/auth/resources.cpp | 24 ++++---- src/auth/utils.cpp | 101 +++++++++++++++++++++++++--------- src/auth/utils.h | 1 + src/main.cpp | 7 ++- src/server/connection.cpp | 11 +--- src/server/reply.cpp | 1 - src/server/request.hpp | 17 ++++-- src/server/request_parser.cpp | 42 +++++++++++++- src/server/request_parser.hpp | 5 +- 10 files changed, 150 insertions(+), 61 deletions(-) diff --git a/src/auth/jwt.cpp b/src/auth/jwt.cpp index 60f20c6..1067181 100644 --- a/src/auth/jwt.cpp +++ b/src/auth/jwt.cpp @@ -74,7 +74,7 @@ std::string http::auth::jwt::Jwt::getUsername() { std::string http::auth::jwt::Jwt::asCookie() { signature = utils::sha256(this->payload + secretKey); auto val = utils::b64Encode(payload) + "." + signature; - return val + ";Path=/; Max-Age=86400; HttpOnly; SameSite=Lax"; + return "auth=" + val + ";Path=/; Max-Age=86400; HttpOnly; SameSite=Lax"; } http::auth::jwt::Jwt::~Jwt() = default; diff --git a/src/auth/resources.cpp b/src/auth/resources.cpp index 58d0ebb..202e09d 100644 --- a/src/auth/resources.cpp +++ b/src/auth/resources.cpp @@ -5,7 +5,8 @@ #include "utils.h" -http::auth::User::User(const std::string &username, const std::string &passwordHash): username(username), passwordHash(passwordHash) {} +http::auth::User::User(const std::string &username, const std::string &passwordHash): username(username), + passwordHash(passwordHash.empty() ? utils::sha256(username) : passwordHash) {} bool http::auth::User::checkPassword(const std::string &pass) const { return utils::sha256(pass) == passwordHash; @@ -57,21 +58,16 @@ std::shared_ptr http::auth::AuthProvider::doAuth(const std::st } std::shared_ptr http::auth::AuthProvider::getSession(const server::Request &req) { - for (const auto& header: req.headers) { - if (boost::iequals(header.name, "cookie")) { - auto t = jwt::Jwt::fromCookies(header.value); - if (t.isValid()) { - const auto name = t.getUsername(); - // токен валидный, ищем юзера - for (auto& u: users) { - if (u->username == name) { - return u; - } - } - - BOOST_LOG_TRIVIAL(warning) << "http::auth::AuthProvider::getSession(): Found valid session for a non-existent user " << name; + auto t = jwt::Jwt::fromCookies(req.getHeaderValue("cookie")); + if (t.isValid()) { + const auto name = t.getUsername(); + // токен валидный, ищем юзера + for (auto& u: users) { + if (u->username == name) { + return u; } } + BOOST_LOG_TRIVIAL(warning) << "http::auth::AuthProvider::getSession(): Found valid session for a non-existent user " << name; } return nullptr; } diff --git a/src/auth/utils.cpp b/src/auth/utils.cpp index e484c07..32fcc55 100644 --- a/src/auth/utils.cpp +++ b/src/auth/utils.cpp @@ -1,11 +1,13 @@ #include "utils.h" + +#include #include #include -#include #include #include #include + std::string http::utils::sha256(const std::string &payload) { // Вычисляем SHA256 хеш unsigned char hash[SHA256_DIGEST_LENGTH]; @@ -19,37 +21,82 @@ std::string http::utils::sha256(const std::string &payload) { return ss.str(); } -std::string http::utils::b64Encode(const std::string &payload) { - BIO *bio_mem = BIO_new(BIO_s_mem()); - BIO *bio_b64 = BIO_new(BIO_f_base64()); - bio_b64 = BIO_push(bio_b64, bio_mem); - - BIO_write(bio_b64, payload.c_str(), payload.length()); - BIO_flush(bio_b64); - - BUF_MEM *bptr = nullptr; - BIO_get_mem_data(bio_mem, &bptr); - - std::string result(bptr->data, bptr->length); - BIO_free_all(bio_b64); - return result; +inline std::string sha256AsB64(const std::string &payload) { + // Вычисляем SHA256 хеш + unsigned char hash[SHA256_DIGEST_LENGTH]; + SHA256(reinterpret_cast(payload.c_str()), payload.length(), hash); + return http::utils::b64Encode(reinterpret_cast(hash), SHA256_DIGEST_LENGTH); } -std::string http::utils::b64Decode(const std::string &payload) { - BIO *bio_mem = BIO_new(BIO_s_mem()); - BIO *bio_b64 = BIO_new(BIO_f_base64()); - bio_b64 = BIO_push(bio_b64, bio_mem); +static const char b64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - BIO_write(bio_b64, payload.c_str(), static_cast(payload.size())); - BIO_flush(bio_b64); +std::string http::utils::b64Encode(const char* data, size_t size) { + using ::std::string; - const int len = BIO_get_mem_data(bio_mem, NULL); - char buffer[static_cast(len)]; - BIO_read(bio_mem, buffer, len); + // Use = signs so the end is properly padded. + string retval((((size + 2) / 3) * 4), '='); + ::std::size_t outpos = 0; + int bits_collected = 0; + unsigned int accumulator = 0; - std::string result(buffer, static_cast(len)); - BIO_free_all(bio_b64); - return result; + for (size_t index = 0; index < size; index++) { + const char i = *(data + index); + accumulator = (accumulator << 8) | (i & 0xff); + bits_collected += 8; + while (bits_collected >= 6) { + bits_collected -= 6; + retval[outpos++] = b64_table[(accumulator >> bits_collected) & 0x3fu]; + } + } + if (bits_collected > 0) { + // Any trailing bits that are missing. + assert(bits_collected < 6); + accumulator <<= 6 - bits_collected; + retval[outpos++] = b64_table[accumulator & 0x3fu]; + } + assert(outpos >= (retval.size() - 2)); + assert(outpos <= retval.size()); + return retval; +} + +std::string http::utils::b64Encode(const std::string &payload) { + return b64Encode(payload.c_str(), payload.size()); +} + +std::string http::utils::b64Decode(const std::string &ascdata) { + static const unsigned char reverse_table[128] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64 + }; + + using ::std::string; + string retval; + int bits_collected = 0; + unsigned int accumulator = 0; + + for (const auto cd: ascdata) { + const auto c = static_cast(cd); + if (::std::isspace(c) || c == '=') { + // Skip whitespace and padding. Be liberal in what you accept. + continue; + } + if ((c > 127) || (c < 0) || (reverse_table[c] > 63)) { + throw ::std::invalid_argument("This contains characters not legal in a base64 encoded string."); + } + accumulator = (accumulator << 6) | reverse_table[c]; + bits_collected += 6; + if (bits_collected >= 8) { + bits_collected -= 8; + retval += static_cast((accumulator >> bits_collected) & 0xffu); + } + } + return retval; } std::map http::utils::parseCookies(const std::string& cookieString) { diff --git a/src/auth/utils.h b/src/auth/utils.h index 4c64a2c..0fe3ba9 100644 --- a/src/auth/utils.h +++ b/src/auth/utils.h @@ -8,6 +8,7 @@ namespace http::utils { std::string sha256(const std::string& payload); std::string sha256AsB64(const std::string& payload); + std::string b64Encode(const char* data, size_t size); std::string b64Encode(const std::string& payload); std::string b64Decode(const std::string& payload); diff --git a/src/main.cpp b/src/main.cpp index a3eae26..1a199df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -134,17 +134,18 @@ public: rep.headers.clear(); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); try { - std::istringstream is(req.body); + std::istringstream is(std::string(req.payload.data(), req.payload.size())); boost::property_tree::ptree pt; - boost::property_tree::read_json(is, pt); + read_json(is, pt); - auto u = auth.doAuth(req); + auto u = auth.doAuth(pt.get("username"), pt.get("password"), rep); if (u == nullptr) { throw std::runtime_error("invalid session"); } std::string result = R"({"redirect":"/"})"; rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); } catch (std::exception &e) { + BOOST_LOG_TRIVIAL(error) << e.what() << std::endl; std::string result = R"({"error":"Неверный логин или пароль"})"; rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); } diff --git a/src/server/connection.cpp b/src/server/connection.cpp index aeb6eb9..da52ebe 100644 --- a/src/server/connection.cpp +++ b/src/server/connection.cpp @@ -24,12 +24,7 @@ namespace http::server { void Connection::doRead() { request_parser_.reset(); - request_.headers.clear(); - request_.method.clear(); - request_.queryUri.clear(); - if (request_.url != nullptr) { - request_.url.reset(nullptr); - } + request_.reset(); auto self(shared_from_this()); socket_.async_read_some(boost::asio::buffer(buffer_), [this, self](boost::system::error_code ec, std::size_t bytes_transferred) { @@ -98,9 +93,7 @@ namespace http::server { get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); request_parser_.reset(); - request_.headers.clear(); - request_.method.clear(); - request_.queryUri.clear(); + request_.reset(); auto self(shared_from_this()); stream_.async_read_some(boost::asio::buffer(buffer_), [this, self](boost::system::error_code ec, std::size_t bytes_transferred) { diff --git a/src/server/reply.cpp b/src/server/reply.cpp index e07d477..c2c5dc5 100644 --- a/src/server/reply.cpp +++ b/src/server/reply.cpp @@ -221,7 +221,6 @@ namespace http::server { void httpRedirect(Reply &rep, const std::string &location) { rep.status = see_other_redirect; rep.content.clear(); - rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::text_html)}); rep.headers.push_back({.name = "Location", .value = location}); } } // namespace http::server diff --git a/src/server/request.hpp b/src/server/request.hpp index 425adf3..b3dbb1a 100644 --- a/src/server/request.hpp +++ b/src/server/request.hpp @@ -19,14 +19,23 @@ namespace http::server { }; /// A request received from a client. - struct Request { + class Request { + public: + Request(); + + void reset(); + std::string getHeaderValue(const std::string& headerName) const; + std::string method; std::string queryUri; std::unique_ptr url; - bool is_keep_alive; - int http_version_major; - int http_version_minor; + bool is_keep_alive{}; + int http_version_major{}; + int http_version_minor{}; std::vector
headers; + std::vector payload; + + ~Request(); }; } // namespace http::Server diff --git a/src/server/request_parser.cpp b/src/server/request_parser.cpp index fa90f85..aebc060 100644 --- a/src/server/request_parser.cpp +++ b/src/server/request_parser.cpp @@ -32,6 +32,32 @@ namespace http::server { Url::~Url() = default; + Request::Request() = default; + + void Request::reset() { + method = ""; + queryUri = ""; + if (url != nullptr) { + url.reset(nullptr); + } + is_keep_alive = false; + http_version_major = 0; + http_version_minor = 0; + headers.clear(); + payload.clear(); + } + + std::string Request::getHeaderValue(const std::string &headerName) const { + for (const auto& header: headers) { + if (boost::iequals(header.name, headerName)) { + return header.value; + } + } + return ""; + } + + Request::~Request() = default; + RequestParser::RequestParser() : state_(method_start) { @@ -39,6 +65,7 @@ namespace http::server { void RequestParser::reset() { state_ = method_start; + contentLenghtHeader = 0; } RequestParser::result_type RequestParser::consume(Request &req, char input) { @@ -216,9 +243,22 @@ namespace http::server { case expecting_newline_3: if (input == '\n') { req.url = std::make_unique(req.queryUri); - return good; + auto content_len = req.getHeaderValue("content-length"); + if (content_len.empty()) { + return good; + } + contentLenghtHeader = std::stol(content_len); + state_ = expecting_payload; + return indeterminate; } return bad; + case expecting_payload: + req.payload.push_back(input); + if (req.payload.size() <= contentLenghtHeader - 1) { + return indeterminate; + } + return good; + default: return bad; } diff --git a/src/server/request_parser.hpp b/src/server/request_parser.hpp index 2b529c1..e5ccbca 100644 --- a/src/server/request_parser.hpp +++ b/src/server/request_parser.hpp @@ -73,8 +73,11 @@ namespace http::server { space_before_header_value, header_value, expecting_newline_2, - expecting_newline_3 + expecting_newline_3, + expecting_payload } state_; + + size_t contentLenghtHeader = 0; }; } // namespace http::Server