diff --git a/RestInterface/cafRestSchemaService.cpp b/RestInterface/cafRestSchemaService.cpp index f3e7f684..6cb65035 100644 --- a/RestInterface/cafRestSchemaService.cpp +++ b/RestInterface/cafRestSchemaService.cpp @@ -32,6 +32,39 @@ #include using namespace caffa::rpc; +using namespace std::chrono_literals; + +constexpr std::chrono::seconds RATE_LIMITER_TIME_PERIOD = 1s; +constexpr size_t RATE_LIMITER_MAX_REQUESTS = 5; + +std::mutex RestSchemaService::s_requestMutex; +std::list RestSchemaService::s_requestTimes; + +bool RestSchemaService::refuseDueToTimeLimiter() +{ + std::scoped_lock lock( s_requestMutex ); + + auto now = std::chrono::steady_clock::now(); + + std::list recentRequests; + for ( auto requestTime : s_requestTimes ) + { + if ( now - requestTime < RATE_LIMITER_TIME_PERIOD ) + { + recentRequests.push_back( requestTime ); + } + } + + s_requestTimes.swap( recentRequests ); + + if ( s_requestTimes.size() >= RATE_LIMITER_MAX_REQUESTS ) + { + return true; + } + + s_requestTimes.push_back( now ); + return false; +} RestSchemaService::ServiceResponse RestSchemaService::perform( http::verb verb, const std::list& path, @@ -61,7 +94,10 @@ RestSchemaService::ServiceResponse RestSchemaService::perform( http::verb if ( !session && RestServerApplication::instance()->requiresValidSession() ) { - return std::make_tuple( http::status::forbidden, "No session provided", nullptr ); + if ( refuseDueToTimeLimiter() ) + { + return std::make_tuple( http::status::too_many_requests, "Too many unauthenticated schema requests", nullptr ); + } } if ( path.empty() ) diff --git a/RestInterface/cafRestSchemaService.h b/RestInterface/cafRestSchemaService.h index 69587d60..5875ded8 100644 --- a/RestInterface/cafRestSchemaService.h +++ b/RestInterface/cafRestSchemaService.h @@ -22,6 +22,9 @@ #include +#include +#include +#include #include #include @@ -55,6 +58,12 @@ class RestSchemaService : public RestServiceInterface static ServiceResponse getFieldSchema( const caffa::ObjectHandle* object, const std::string& fieldName ); static ServiceResponse getAllSchemas(); + +private: + static bool refuseDueToTimeLimiter(); + + static std::list s_requestTimes; + static std::mutex s_requestMutex; }; } // namespace caffa::rpc