From 2925b2e8d182aa55f702ed2bc1e9e75ce62d055c Mon Sep 17 00:00:00 2001 From: Kishore Nallan Date: Thu, 7 Jul 2022 09:37:27 +0530 Subject: [PATCH 1/4] Store auth inside request. --- include/http_data.h | 7 ++++--- include/http_server.h | 1 - src/http_server.cpp | 16 +++++++--------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/http_data.h b/include/http_data.h index 1084ebe3..3b6c08cc 100644 --- a/include/http_data.h +++ b/include/http_data.h @@ -210,6 +210,7 @@ struct http_req { uint64_t route_hash; std::map params; std::vector embedded_params_vec; + std::string api_auth_key; bool first_chunk_aggregate; std::atomic last_chunk_aggregate; @@ -247,10 +248,10 @@ struct http_req { } http_req(h2o_req_t* _req, const std::string & http_method, const std::string & path_without_query, uint64_t route_hash, - const std::map& params, - std::vector& embedded_params_vec, const std::string& body, const std::string& client_ip): + const std::map& params, std::vector& embedded_params_vec, + const std::string& api_auth_key, const std::string& body, const std::string& client_ip): _req(_req), http_method(http_method), path_without_query(path_without_query), route_hash(route_hash), - params(params), embedded_params_vec(embedded_params_vec), + params(params), embedded_params_vec(embedded_params_vec), api_auth_key(api_auth_key), first_chunk_aggregate(true), last_chunk_aggregate(false), chunk_len(0), body(body), body_index(0), data(nullptr), ready(false), log_index(0), is_diposed(false), client_ip(client_ip) { diff --git a/include/http_server.h b/include/http_server.h index 0b976cc2..ba4421ac 100644 --- a/include/http_server.h +++ b/include/http_server.h @@ -22,7 +22,6 @@ class HttpServer; struct h2o_custom_req_handler_t { h2o_handler_t super; HttpServer* http_server; - std::string api_auth_key_sent; }; struct h2o_custom_generator_t { diff --git a/src/http_server.cpp b/src/http_server.cpp index 0689c939..c6c53d36 100644 --- a/src/http_server.cpp +++ b/src/http_server.cpp @@ -278,7 +278,6 @@ void HttpServer::on_res_generator_dispose(void *self) { } // without this, warning about memory allocated by std::string leaking happens - std::string().swap(custom_generator->h2o_handler->api_auth_key_sent); delete custom_generator; } @@ -398,14 +397,13 @@ int HttpServer::catch_all_handler(h2o_handler_t *_h2o_handler, h2o_req_t *req) { // Extract auth key from header. If that does not exist, look for a GET parameter. ssize_t auth_header_cursor = h2o_find_header_by_str(&req->headers, http_req::AUTH_HEADER, strlen(http_req::AUTH_HEADER), -1); + std::string api_auth_key_sent; if(auth_header_cursor != -1) { h2o_iovec_t & slot = req->headers.entries[auth_header_cursor].value; - const std::string api_auth_key_sent = std::string(slot.base, slot.len); - // NOTE: directly using `h2o_handler->api_auth_key_sent` without an intermediate string causes memory errors - h2o_handler->api_auth_key_sent = api_auth_key_sent; + api_auth_key_sent = std::string(slot.base, slot.len); } else if(query_map.count(http_req::AUTH_HEADER) != 0) { - h2o_handler->api_auth_key_sent = query_map[http_req::AUTH_HEADER]; + api_auth_key_sent = query_map[http_req::AUTH_HEADER]; } route_path *rpath = nullptr; @@ -460,7 +458,7 @@ int HttpServer::catch_all_handler(h2o_handler_t *_h2o_handler, h2o_req_t *req) { // multi_search needs to be handled later because the API key could be part of request body and // the whole request body might not be available right now. bool authenticated = h2o_handler->http_server->auth_handler(query_map, embedded_params_vec, body, *rpath, - h2o_handler->api_auth_key_sent); + api_auth_key_sent); if(!authenticated) { std::string message = std::string("{\"message\": \"Forbidden - a valid `") + http_req::AUTH_HEADER + "` header must be sent.\"}"; @@ -469,8 +467,8 @@ int HttpServer::catch_all_handler(h2o_handler_t *_h2o_handler, h2o_req_t *req) { } std::shared_ptr request = std::make_shared(req, rpath->http_method, path_without_query, - route_hash, query_map, embedded_params_vec, body, - client_ip); + route_hash, query_map, embedded_params_vec, + api_auth_key_sent, body, client_ip); // add custom generator with a dispose function for cleaning up resources h2o_custom_generator_t* custom_gen = new h2o_custom_generator_t; @@ -656,7 +654,7 @@ int HttpServer::process_request(const std::shared_ptr& request, const if(root_resource == "multi_search") { // We can authenticate only when the full request body is available bool authenticated = handler->http_server->auth_handler(request->params, request->embedded_params_vec, - request->body, *rpath, handler->api_auth_key_sent); + request->body, *rpath, request->api_auth_key); if(!authenticated) { std::string message = std::string("{\"message\": \"Forbidden - a valid `") + http_req::AUTH_HEADER + "` header must be sent.\"}"; From d05c51c5361a9f60627b53b5d2283fc389dcb419 Mon Sep 17 00:00:00 2001 From: Kishore Nallan Date: Thu, 7 Jul 2022 09:53:50 +0530 Subject: [PATCH 2/4] Handle bad regexp in allowed collection API key. --- include/auth_manager.h | 2 ++ src/auth_manager.cpp | 11 ++++++++++- test/auth_manager_test.cpp | 7 +++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/auth_manager.h b/include/auth_manager.h index 94c8704a..eb7380ce 100644 --- a/include/auth_manager.h +++ b/include/auth_manager.h @@ -117,6 +117,8 @@ private: bool auth_against_key(const std::string& req_collection, const std::string& action, const api_key_t &api_key, const bool search_only) const; + static bool regexp_match(const std::string& value, const std::string& regexp); + public: static const size_t GENERATED_KEY_LEN = 32; diff --git a/src/auth_manager.cpp b/src/auth_manager.cpp index 4b073365..c763694e 100644 --- a/src/auth_manager.cpp +++ b/src/auth_manager.cpp @@ -176,6 +176,15 @@ bool AuthManager::authenticate(const std::string& action, return (num_keys_matched == collection_keys.size()); } +bool AuthManager::regexp_match(const std::string& value, const std::string& regexp) { + try { + return std::regex_match (value, std::regex(regexp)); + } catch(const std::exception& e) { + LOG(ERROR) << "Error while matching regexp " << regexp << " against value " << value; + return false; + } +} + bool AuthManager::auth_against_key(const std::string& req_collection, const std::string& action, const api_key_t& api_key, const bool search_only) const { @@ -221,7 +230,7 @@ bool AuthManager::auth_against_key(const std::string& req_collection, const std: for(const std::string& allowed_collection: api_key.collections) { if(allowed_collection == "*" || (allowed_collection == req_collection) || req_collection.empty() || - std::regex_match (req_collection, std::regex(allowed_collection))) { + regexp_match(req_collection, allowed_collection)) { coll_allowed = true; break; } diff --git a/test/auth_manager_test.cpp b/test/auth_manager_test.cpp index 31b2915a..5db876b3 100644 --- a/test/auth_manager_test.cpp +++ b/test/auth_manager_test.cpp @@ -251,6 +251,13 @@ TEST_F(AuthManagerTest, VerifyAuthentication) { {collection_key_t("collection1", coll_a_key.value), collection_key_t("collectionB", coll_b_key.value)}, sparams, embedded_params)); + + // bad collection allow regexp + api_key_t coll_c_key = api_key_t("coll_c", "one action key", {"documents:search"}, {"*coll_c"}, FUTURE_TS); + auth_manager.create_key(coll_c_key); + ASSERT_FALSE(auth_manager.authenticate("documents:search", + {collection_key_t("coll_c", coll_c_key.value),}, + sparams, embedded_params)); } TEST_F(AuthManagerTest, GenerationOfAPIAction) { From ebb2803af70b94e86f1c720753cddeca142fd1ff Mon Sep 17 00:00:00 2001 From: Kishore Nallan Date: Thu, 7 Jul 2022 10:10:00 +0530 Subject: [PATCH 3/4] Update ARM build image. --- docker-build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-build.sh b/docker-build.sh index 118da11d..059afaa9 100755 --- a/docker-build.sh +++ b/docker-build.sh @@ -33,7 +33,7 @@ TYPESENSE_DEV_IMAGE="typesense-development:27-JUN-2022-1" ARCH_NAME="amd64" if [[ "$@" == *"--graviton2"* ]]; then - TYPESENSE_DEV_IMAGE="typesense-development-arm:03-DEC-2021-1" + TYPESENSE_DEV_IMAGE="typesense-development-arm:27-JUN-2022-1" ARCH_NAME="arm64" fi From 483ed4d533e8a657784472e3a3066298fa085c44 Mon Sep 17 00:00:00 2001 From: Kishore Nallan Date: Thu, 7 Jul 2022 19:28:34 +0530 Subject: [PATCH 4/4] Update README. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b7618d2d..4d7b83b8 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ Here's a quick example showcasing how you can create a collection, index a docum Let's begin by starting the Typesense server via Docker: ``` -docker run -p 8108:8108 -v/tmp/data:/data typesense/typesense:0.23.0 --data-dir /data --api-key=Hu52dwsas2AdxdE +docker run -p 8108:8108 -v/tmp/data:/data typesense/typesense:0.23.1 --data-dir /data --api-key=Hu52dwsas2AdxdE ``` We have [API Clients](#api-clients) in a couple of languages, but let's use the Python client for this example. @@ -232,7 +232,7 @@ We welcome community contributions to add more official client libraries and int You can use our [InstantSearch.js adapter](https://github.com/typesense/typesense-instantsearch-adapter) to quickly build powerful search experiences, complete with filtering, sorting, pagination and more. -Here's how: [https://typesense.org/docs/0.23.0/guide/#search-ui](https://typesense.org/docs/0.23.0/guide/#search-ui) +Here's how: [https://typesense.org/docs/0.23.1/guide/#search-ui](https://typesense.org/docs/0.23.1/guide/#search-ui) ## FAQ