changeset 1961:ef1e9856c26f

New callback to filter incoming HTTP requests: OrthancPluginRegisterIncomingHttpRequestFilter()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 07 Apr 2016 17:26:13 +0200
parents 239e50b3792f
children 22ddb22fce83
files Core/HttpServer/IIncomingHttpRequestFilter.h Core/HttpServer/MongooseServer.h NEWS Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/OrthancPlugins.h Plugins/Engine/PluginsEnumerations.cpp Plugins/Engine/PluginsEnumerations.h Plugins/Include/orthanc/OrthancCPlugin.h
diffstat 8 files changed, 212 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- /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 <http://www.gnu.org/licenses/>.
+ **/
+
+
+#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;
+  };
+}
--- 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()
--- 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
 ---
 
--- 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<RestCallback*>  RestCallbacks;
     typedef std::list<OrthancPluginOnStoredInstanceCallback>  OnStoredCallbacks;
     typedef std::list<OrthancPluginOnChangeCallback>  OnChangeCallbacks;
+    typedef std::list<OrthancPluginIncomingHttpRequestFilter>  IncomingHttpRequestFilters;
     typedef std::map<Property, std::string>  Properties;
 
     PluginsManager manager_;
@@ -292,6 +293,7 @@
     OnChangeCallbacks  onChangeCallbacks_;
     OrthancPluginWorklistCallback  worklistCallback_;
     OrthancPluginDecodeImageCallback  decodeImageCallback_;
+    IncomingHttpRequestFilters  incomingHttpRequestFilters_;
     std::auto_ptr<StorageAreaFactory>  storageArea_;
     boost::recursive_mutex restCallbackMutex_;
     boost::recursive_mutex storedCallbackMutex_;
@@ -759,6 +761,14 @@
   }
 
 
+  void OrthancPlugins::RegisterIncomingHttpRequestFilter(const void* parameters)
+  {
+    const _OrthancPluginIncomingHttpRequestFilter& p = 
+      *reinterpret_cast<const _OrthancPluginIncomingHttpRequestFilter*>(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<const char*> httpKeys;
+    std::vector<const char*> 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;
+  }
 }
--- 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;
   };
 }
 
--- 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)
     {
--- 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);
 
--- 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().
  * -# <tt>void OrthancPluginFinalize()</tt>:
  *    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, &params);
+  }
+  
+
 #ifdef  __cplusplus
 }
 #endif