# HG changeset patch # User Sebastien Jodogne # Date 1367576582 -7200 # Node ID 63f707278fc86b0a67a92a77c7e9de682d8b17b0 # Parent 5a3a4a25e56803102b5c580b5e466bbe836f97cb lua filtering of incoming http requests diff -r 5a3a4a25e568 -r 63f707278fc8 Core/HttpServer/MongooseServer.cpp --- a/Core/HttpServer/MongooseServer.cpp Thu May 02 16:51:40 2013 +0200 +++ b/Core/HttpServer/MongooseServer.cpp Fri May 03 12:23:02 2013 +0200 @@ -454,6 +454,37 @@ } + static std::string GetAuthenticatedUsername(const HttpHandler::Arguments& headers) + { + HttpHandler::Arguments::const_iterator auth = headers.find("authorization"); + + if (auth == headers.end()) + { + return ""; + } + + std::string s = auth->second; + if (s.substr(0, 6) != "Basic ") + { + return ""; + } + + std::string b64 = s.substr(6); + std::string decoded = Toolbox::DecodeBase64(b64); + size_t semicolons = decoded.find(':'); + + if (semicolons == std::string::npos) + { + // Bad-formatted request + return ""; + } + else + { + return decoded.substr(0, semicolons); + } + } + + static void* Callback(enum mg_event event, struct mg_connection *connection, @@ -511,6 +542,28 @@ return (void*) ""; } + + // Apply the filter, if it is installed + const IIncomingHttpRequestFilter *filter = that->GetIncomingHttpRequestFilter(); + if (filter != NULL) + { + std::string username = GetAuthenticatedUsername(headers); + + char remoteIp[24]; + sprintf(remoteIp, "%d.%d.%d.%d", + reinterpret_cast(&request->remote_ip) [3], + reinterpret_cast(&request->remote_ip) [2], + reinterpret_cast(&request->remote_ip) [1], + reinterpret_cast(&request->remote_ip) [0]); + + if (!filter->IsAllowed(method, request->uri, remoteIp, username.c_str())) + { + SendUnauthorized(output); + return (void*) ""; + } + } + + std::string postData; if (method == Orthanc_HttpMethod_Get) @@ -737,6 +790,11 @@ remoteAllowed_ = allowed; } + void MongooseServer::SetIncomingHttpRequestFilter(IIncomingHttpRequestFilter& filter) + { + Stop(); + filter_ = &filter; + } bool MongooseServer::IsValidBasicHttpAuthentication(const std::string& basic) const { diff -r 5a3a4a25e568 -r 63f707278fc8 Core/HttpServer/MongooseServer.h --- a/Core/HttpServer/MongooseServer.h Thu May 02 16:51:40 2013 +0200 +++ b/Core/HttpServer/MongooseServer.h Fri May 03 12:23:02 2013 +0200 @@ -44,6 +44,19 @@ { class ChunkStore; + class IIncomingHttpRequestFilter + { + public: + virtual ~IIncomingHttpRequestFilter() + { + } + + virtual bool IsAllowed(Orthanc_HttpMethod method, + const char* uri, + const char* ip, + const char* username) const = 0; + }; + class MongooseServer { private: @@ -62,6 +75,7 @@ bool ssl_; std::string certificate_; uint16_t port_; + IIncomingHttpRequestFilter* filter_; bool IsRunning() const; @@ -116,6 +130,13 @@ void SetRemoteAccessAllowed(bool allowed); + const IIncomingHttpRequestFilter* GetIncomingHttpRequestFilter() const + { + return filter_; + } + + void SetIncomingHttpRequestFilter(IIncomingHttpRequestFilter& filter); + void ClearHandlers(); // Can return NULL if no handler is associated to this URI diff -r 5a3a4a25e568 -r 63f707278fc8 NEWS --- a/NEWS Thu May 02 16:51:40 2013 +0200 +++ b/NEWS Fri May 03 12:23:02 2013 +0200 @@ -5,6 +5,8 @@ * Store-SCU for patients and studies in Orthanc Explorer. * "Bulk" Store-SCU (send several DICOM instances with the same DICOM connexion). +* Filtering of incoming DICOM instances (through Lua scripting). +* Filtering of incoming HTTP requests (through Lua scripting). Version 0.5.1 (2013/04/17) diff -r 5a3a4a25e568 -r 63f707278fc8 OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Thu May 02 16:51:40 2013 +0200 +++ b/OrthancServer/ServerContext.cpp Fri May 03 12:23:02 2013 +0200 @@ -100,7 +100,7 @@ if (!call.ExecutePredicate()) { - LOG(INFO) << "An incoming instance has been discarded by a filter"; + LOG(INFO) << "An incoming instance has been discarded by the filter"; return StoreStatus_FilteredOut; } } diff -r 5a3a4a25e568 -r 63f707278fc8 OrthancServer/main.cpp --- a/OrthancServer/main.cpp Thu May 02 16:51:40 2013 +0200 +++ b/OrthancServer/main.cpp Fri May 03 12:23:02 2013 +0200 @@ -148,6 +148,66 @@ }; +class MyIncomingHttpRequestFilter : public IIncomingHttpRequestFilter +{ +private: + ServerContext& context_; + +public: + MyIncomingHttpRequestFilter(ServerContext& context) : context_(context) + { + } + + virtual bool IsAllowed(Orthanc_HttpMethod method, + const char* uri, + const char* ip, + const char* username) const + { + static const char* HTTP_FILTER = "IncomingHttpRequestFilter"; + + // Test if the instance must be filtered out + if (context_.GetLuaContext().IsExistingFunction(HTTP_FILTER)) + { + LuaFunctionCall call(context_.GetLuaContext(), HTTP_FILTER); + + switch (method) + { + case Orthanc_HttpMethod_Get: + call.PushString("GET"); + break; + + case Orthanc_HttpMethod_Put: + call.PushString("PUT"); + break; + + case Orthanc_HttpMethod_Post: + call.PushString("POST"); + break; + + case Orthanc_HttpMethod_Delete: + call.PushString("DELETE"); + break; + + default: + return true; + } + + call.PushString(uri); + call.PushString(ip); + call.PushString(username); + + if (!call.ExecutePredicate()) + { + LOG(INFO) << "An incoming HTTP request has been discarded by the filter"; + return false; + } + } + + return true; + } +}; + + void PrintHelp(char* path) { std::cout @@ -321,9 +381,11 @@ dicomServer.SetApplicationEntityTitle(GetGlobalStringParameter("DicomAet", "ORTHANC")); // HTTP server + MyIncomingHttpRequestFilter httpFilter(context); MongooseServer httpServer; httpServer.SetPortNumber(GetGlobalIntegerParameter("HttpPort", 8042)); httpServer.SetRemoteAccessAllowed(GetGlobalBoolParameter("RemoteAccessAllowed", false)); + httpServer.SetIncomingHttpRequestFilter(httpFilter); httpServer.SetAuthenticationEnabled(GetGlobalBoolParameter("AuthenticationEnabled", false)); SetupRegisteredUsers(httpServer);