# HG changeset patch # User Sebastien Jodogne # Date 1460042773 -7200 # Node ID ef1e9856c26f563b9c4bed3038470dd9174ef218 # Parent 239e50b3792ff3c9325dc08a5b4fed37b984e935 New callback to filter incoming HTTP requests: OrthancPluginRegisterIncomingHttpRequestFilter() diff -r 239e50b3792f -r ef1e9856c26f Core/HttpServer/IIncomingHttpRequestFilter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/HttpServer/IIncomingHttpRequestFilter.h Thu Apr 07 17:26:13 2016 +0200 @@ -0,0 +1,52 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IHttpHandler.h" + +namespace Orthanc +{ + class IIncomingHttpRequestFilter : public boost::noncopyable + { + public: + virtual ~IIncomingHttpRequestFilter() + { + } + + virtual bool IsAllowed(HttpMethod method, + const char* uri, + const char* ip, + const char* username, + const IHttpHandler::Arguments& httpHeaders) const = 0; + }; +} diff -r 239e50b3792f -r ef1e9856c26f Core/HttpServer/MongooseServer.h --- a/Core/HttpServer/MongooseServer.h Thu Apr 07 16:31:33 2016 +0200 +++ b/Core/HttpServer/MongooseServer.h Thu Apr 07 17:26:13 2016 +0200 @@ -32,7 +32,7 @@ #pragma once -#include "IHttpHandler.h" +#include "IIncomingHttpRequestFilter.h" #include "../OrthancException.h" @@ -46,22 +46,7 @@ { class ChunkStore; - class IIncomingHttpRequestFilter - { - public: - virtual ~IIncomingHttpRequestFilter() - { - } - - virtual bool IsAllowed(HttpMethod method, - const char* uri, - const char* ip, - const char* username, - const IHttpHandler::Arguments& httpHeaders) const = 0; - }; - - - class IHttpExceptionFormatter + class IHttpExceptionFormatter : public boost::noncopyable { public: virtual ~IHttpExceptionFormatter() diff -r 239e50b3792f -r ef1e9856c26f NEWS --- a/NEWS Thu Apr 07 16:31:33 2016 +0200 +++ b/NEWS Thu Apr 07 17:26:13 2016 +0200 @@ -18,6 +18,11 @@ * Support decoding of RLE Lossless transfer syntax * Support of signed 16bpp images in ParsedDicomFile +Plugins +------- + +* New callback to filter incoming HTTP requests: OrthancPluginRegisterIncomingHttpRequestFilter() + Lua --- diff -r 239e50b3792f -r ef1e9856c26f Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Thu Apr 07 16:31:33 2016 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Thu Apr 07 17:26:13 2016 +0200 @@ -283,6 +283,7 @@ typedef std::list RestCallbacks; typedef std::list OnStoredCallbacks; typedef std::list OnChangeCallbacks; + typedef std::list IncomingHttpRequestFilters; typedef std::map Properties; PluginsManager manager_; @@ -292,6 +293,7 @@ OnChangeCallbacks onChangeCallbacks_; OrthancPluginWorklistCallback worklistCallback_; OrthancPluginDecodeImageCallback decodeImageCallback_; + IncomingHttpRequestFilters incomingHttpRequestFilters_; std::auto_ptr storageArea_; boost::recursive_mutex restCallbackMutex_; boost::recursive_mutex storedCallbackMutex_; @@ -759,6 +761,14 @@ } + void OrthancPlugins::RegisterIncomingHttpRequestFilter(const void* parameters) + { + const _OrthancPluginIncomingHttpRequestFilter& p = + *reinterpret_cast(parameters); + + LOG(INFO) << "Plugin has registered a callback to filter incoming HTTP requests"; + pimpl_->incomingHttpRequestFilters_.push_back(p.callback); + } void OrthancPlugins::AnswerBuffer(const void* parameters) @@ -1762,6 +1772,10 @@ RegisterDecodeImageCallback(parameters); return true; + case _OrthancPluginService_RegisterIncomingHttpRequestFilter: + RegisterIncomingHttpRequestFilter(parameters); + return true; + case _OrthancPluginService_AnswerBuffer: AnswerBuffer(parameters); return true; @@ -2409,4 +2423,50 @@ DefaultDicomImageDecoder defaultDecoder; return defaultDecoder.Decode(dicom, size, frame); // TODO RETURN NULL ??? } + + + bool OrthancPlugins::IsAllowed(HttpMethod method, + const char* uri, + const char* ip, + const char* username, + const IHttpHandler::Arguments& httpHeaders) const + { + std::vector httpKeys; + std::vector httpValues; + + httpKeys.reserve(httpHeaders.size()); + httpValues.reserve(httpHeaders.size()); + + size_t pos = 0; + for (IHttpHandler::Arguments::const_iterator + it = httpHeaders.begin(); it != httpHeaders.end(); ++it, pos++) + { + httpKeys[pos] = it->first.c_str(); + httpValues[pos] = it->first.c_str(); + } + + OrthancPluginHttpMethod cMethod = Plugins::Convert(method); + const char** cHttpKeys = (httpKeys.size() == 0 ? NULL : &httpKeys[0]); + const char** cHttpValues = (httpValues.size() == 0 ? NULL : &httpValues[0]); + + for (PImpl::IncomingHttpRequestFilters::const_iterator + filter = pimpl_->incomingHttpRequestFilters_.begin(); + filter != pimpl_->incomingHttpRequestFilters_.end(); ++filter) + { + int32_t allowed = (*filter) (cMethod, uri, ip, httpKeys.size(), cHttpKeys, cHttpValues); + + if (allowed != 0 && + allowed != 1) + { + throw OrthancException(ErrorCode_Plugin); + } + + if (allowed == 0) + { + return false; + } + } + + return true; + } } diff -r 239e50b3792f -r ef1e9856c26f Plugins/Engine/OrthancPlugins.h --- a/Plugins/Engine/OrthancPlugins.h Thu Apr 07 16:31:33 2016 +0200 +++ b/Plugins/Engine/OrthancPlugins.h Thu Apr 07 17:26:13 2016 +0200 @@ -49,6 +49,7 @@ #include "../../Core/FileStorage/IStorageArea.h" #include "../../Core/HttpServer/IHttpHandler.h" +#include "../../Core/HttpServer/IIncomingHttpRequestFilter.h" #include "../../OrthancServer/IServerListener.h" #include "../../OrthancServer/IDicomImageDecoder.h" #include "../../OrthancServer/DicomProtocol/IWorklistRequestHandlerFactory.h" @@ -67,7 +68,8 @@ public IPluginServiceProvider, public IServerListener, public IWorklistRequestHandlerFactory, - public IDicomImageDecoder + public IDicomImageDecoder, + public IIncomingHttpRequestFilter { private: struct PImpl; @@ -88,6 +90,8 @@ void RegisterDecodeImageCallback(const void* parameters); + void RegisterIncomingHttpRequestFilter(const void* parameters); + void AnswerBuffer(const void* parameters); void Redirect(const void* parameters); @@ -239,6 +243,12 @@ virtual ImageAccessor* Decode(const void* dicom, size_t size, unsigned int frame); + + virtual bool IsAllowed(HttpMethod method, + const char* uri, + const char* ip, + const char* username, + const IHttpHandler::Arguments& httpHeaders) const; }; } diff -r 239e50b3792f -r ef1e9856c26f Plugins/Engine/PluginsEnumerations.cpp --- a/Plugins/Engine/PluginsEnumerations.cpp Thu Apr 07 16:31:33 2016 +0200 +++ b/Plugins/Engine/PluginsEnumerations.cpp Thu Apr 07 17:26:13 2016 +0200 @@ -304,6 +304,28 @@ } + OrthancPluginHttpMethod Convert(HttpMethod method) + { + switch (method) + { + case HttpMethod_Get: + return OrthancPluginHttpMethod_Get; + + case HttpMethod_Post: + return OrthancPluginHttpMethod_Post; + + case HttpMethod_Put: + return OrthancPluginHttpMethod_Put; + + case HttpMethod_Delete: + return OrthancPluginHttpMethod_Delete; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + #if !defined(ORTHANC_ENABLE_DCMTK) || ORTHANC_ENABLE_DCMTK != 0 DcmEVR Convert(OrthancPluginValueRepresentation vr) { diff -r 239e50b3792f -r ef1e9856c26f Plugins/Engine/PluginsEnumerations.h --- a/Plugins/Engine/PluginsEnumerations.h Thu Apr 07 16:31:33 2016 +0200 +++ b/Plugins/Engine/PluginsEnumerations.h Thu Apr 07 17:26:13 2016 +0200 @@ -67,6 +67,8 @@ OrthancPluginInstanceOrigin Convert(RequestOrigin origin); + OrthancPluginHttpMethod Convert(HttpMethod method); + #if !defined(ORTHANC_ENABLE_DCMTK) || ORTHANC_ENABLE_DCMTK != 0 DcmEVR Convert(OrthancPluginValueRepresentation vr); diff -r 239e50b3792f -r ef1e9856c26f Plugins/Include/orthanc/OrthancCPlugin.h --- a/Plugins/Include/orthanc/OrthancCPlugin.h Thu Apr 07 16:31:33 2016 +0200 +++ b/Plugins/Include/orthanc/OrthancCPlugin.h Thu Apr 07 17:26:13 2016 +0200 @@ -20,6 +20,7 @@ * - Possibly register a custom database back-end area using OrthancPluginRegisterDatabaseBackendV2(). * - Possibly register a handler for C-Find SCP against DICOM worklists using OrthancPluginRegisterWorklistCallback(). * - Possibly register a custom decoder for DICOM images using OrthancPluginRegisterDecodeImageCallback(). + * - Possibly register a callback to filter incoming HTTP requests using OrthancPluginRegisterIncomingHttpRequestFilter(). * -# void OrthancPluginFinalize(): * This function is invoked by Orthanc during its shutdown. The plugin * must free all its memory. @@ -114,7 +115,7 @@ #define ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER 1 #define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 0 -#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 0 +#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 1 @@ -410,6 +411,7 @@ _OrthancPluginService_RegisterRestCallbackNoLock = 1004, _OrthancPluginService_RegisterWorklistCallback = 1005, _OrthancPluginService_RegisterDecodeImageCallback = 1006, + _OrthancPluginService_RegisterIncomingHttpRequestFilter = 1007, /* Sending answers to REST calls */ _OrthancPluginService_AnswerBuffer = 2000, @@ -951,6 +953,34 @@ /** + * @brief Callback to filter incoming HTTP requests received by Orthanc. + * + * Signature of a callback function that is triggered whenever + * Orthanc receives an HTTP/REST request, and that answers whether + * this request should be allowed. If the callback returns "0" + * ("false"), the server answers with HTTP status code 403 + * (Forbidden). + * + * @param method The HTTP method used by the request. + * @param uri The URI of interest. + * @param ip The IP address of the HTTP client. + * @param headersCount The number of HTTP headers. + * @param headersKeys The keys of the HTTP headers (always converted to low-case). + * @param headersValues The values of the HTTP headers. + * @return 0 if forbidden access, 1 if allowed access, -1 if error. + * @ingroup Callback + **/ + typedef int32_t (*OrthancPluginIncomingHttpRequestFilter) ( + OrthancPluginHttpMethod method, + const char* uri, + const char* ip, + const uint32_t headersCount, + const char* const* headersKeys, + const char* const* headersValues); + + + + /** * @brief Data structure that contains information about the Orthanc core. **/ typedef struct _OrthancPluginContext_t @@ -4731,6 +4761,33 @@ } + typedef struct + { + OrthancPluginIncomingHttpRequestFilter callback; + } _OrthancPluginIncomingHttpRequestFilter; + + /** + * @brief Register a callback to filter incoming HTTP requests. + * + * This function registers a custom callback to filter incoming HTTP/REST + * requests received by the HTTP server of Orthanc. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param callback The callback. + * @return 0 if success, other value if error. + * @ingroup Callbacks + **/ + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRegisterIncomingHttpRequestFilter( + OrthancPluginContext* context, + OrthancPluginIncomingHttpRequestFilter callback) + { + _OrthancPluginIncomingHttpRequestFilter params; + params.callback = callback; + + return context->InvokeService(context, _OrthancPluginService_RegisterIncomingHttpRequestFilter, ¶ms); + } + + #ifdef __cplusplus } #endif