changeset 6243:ec3044e6d41f

added OrthancPluginHttpAuthenticationStatus_Forbidden
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 11 Jul 2025 16:34:33 +0200
parents 67f9625abb90
children f1fe166b21d5
files OrthancFramework/Sources/HttpServer/HttpServer.cpp OrthancFramework/Sources/HttpServer/IIncomingHttpRequestFilter.h OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Plugins/Engine/OrthancPlugins.h OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h OrthancServer/Sources/main.cpp
diffstat 6 files changed, 69 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/HttpServer/HttpServer.cpp	Fri Jul 11 13:58:27 2025 +0200
+++ b/OrthancFramework/Sources/HttpServer/HttpServer.cpp	Fri Jul 11 16:34:33 2025 +0200
@@ -1279,16 +1279,16 @@
 
     if (filter == NULL)
     {
-      status = IIncomingHttpRequestFilter::AuthenticationStatus_NotImplemented;
+      status = IIncomingHttpRequestFilter::AuthenticationStatus_BuiltIn;
     }
     else
     {
-      status = filter->CheckAuthentication(authenticationPayload, redirection, requestUri, argumentsGET, headers);
+      status = filter->CheckAuthentication(authenticationPayload, redirection, requestUri, remoteIp, headers, argumentsGET);
     }
 
     switch (status)
     {
-      case IIncomingHttpRequestFilter::AuthenticationStatus_NotImplemented:
+      case IIncomingHttpRequestFilter::AuthenticationStatus_BuiltIn:
         // This was the only behavior available in Orthanc <= 1.12.8
         if (server.IsAuthenticationEnabled() &&
             accessMode == AccessMode_Unauthorized)
@@ -1298,7 +1298,7 @@
         }
         break;
 
-      case IIncomingHttpRequestFilter::AuthenticationStatus_Success:
+      case IIncomingHttpRequestFilter::AuthenticationStatus_Granted:
         break;
 
       case IIncomingHttpRequestFilter::AuthenticationStatus_Redirect:
@@ -1309,6 +1309,10 @@
         output.SendStatus(HttpStatus_401_Unauthorized);
         return;
 
+      case IIncomingHttpRequestFilter::AuthenticationStatus_Forbidden:
+        output.SendStatus(HttpStatus_403_Forbidden);
+        return;
+
       default:
         throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
--- a/OrthancFramework/Sources/HttpServer/IIncomingHttpRequestFilter.h	Fri Jul 11 13:58:27 2025 +0200
+++ b/OrthancFramework/Sources/HttpServer/IIncomingHttpRequestFilter.h	Fri Jul 11 16:34:33 2025 +0200
@@ -33,10 +33,11 @@
   public:
     enum AuthenticationStatus
     {
-      AuthenticationStatus_NotImplemented,
-      AuthenticationStatus_Success,
-      AuthenticationStatus_Unauthorized,
-      AuthenticationStatus_Redirect
+      AuthenticationStatus_BuiltIn,       // Use the default HTTP authentication built in Orthanc
+      AuthenticationStatus_Granted,       // Let the REST callback process the request
+      AuthenticationStatus_Unauthorized,  // 401 HTTP status
+      AuthenticationStatus_Forbidden,     // 403 HTTP status
+      AuthenticationStatus_Redirect       // 307 HTTP status
     };
 
     virtual ~IIncomingHttpRequestFilter()
@@ -46,14 +47,15 @@
     // New in Orthanc 1.8.1
     virtual bool IsValidBearerToken(const std::string& token) const = 0;
 
-    // This method corresponds to HTTP authentication
+    // This method corresponds to HTTP authentication + HTTP authorization
     virtual AuthenticationStatus CheckAuthentication(std::string& customPayload /* out: payload to provide to "IsAllowed()" */,
                                                      std::string& redirection   /* out: path relative to the root */,
-                                                     const std::string& uri,
-                                                     const HttpToolbox::GetArguments& getArguments,
-                                                     const HttpToolbox::Arguments& httpHeaders) const = 0;
+                                                     const char* uri,
+                                                     const char* ip,
+                                                     const HttpToolbox::Arguments& httpHeaders,
+                                                     const HttpToolbox::GetArguments& getArguments) const = 0;
     
-    // This method corresponds to HTTP authorization
+    // This method corresponds to HTTP authorization alone
     virtual bool IsAllowed(HttpMethod method,
                            const char* uri,
                            const char* ip,
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Fri Jul 11 13:58:27 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Fri Jul 11 16:34:33 2025 +0200
@@ -6873,15 +6873,16 @@
   IIncomingHttpRequestFilter::AuthenticationStatus OrthancPlugins::CheckAuthentication(
     std::string& customPayload,
     std::string& redirection,
-    const std::string& uri,
-    const HttpToolbox::GetArguments& getArguments,
-    const HttpToolbox::Arguments& httpHeaders) const
+    const char* uri,
+    const char* ip,
+    const HttpToolbox::Arguments& httpHeaders,
+    const HttpToolbox::GetArguments& getArguments) const
   {
     boost::shared_lock<boost::shared_mutex> lock(pimpl_->incomingHttpRequestFilterMutex_);
 
     if (pimpl_->httpAuthentication_ == NULL)
     {
-      return IIncomingHttpRequestFilter::AuthenticationStatus_NotImplemented;
+      return IIncomingHttpRequestFilter::AuthenticationStatus_BuiltIn;  // Use the default authentication of Orthanc
     }
     else
     {
@@ -6895,9 +6896,9 @@
       PluginMemoryBuffer32 payloadBuffer;
       PluginMemoryBuffer32 redirectionBuffer;
       OrthancPluginErrorCode code = pimpl_->httpAuthentication_(
-        &status, payloadBuffer.GetObject(), redirectionBuffer.GetObject(), uri.c_str(),
-        getKeys.size(), getKeys.empty() ? NULL : &getKeys[0], getValues.empty() ? NULL : &getValues[0],
-        headersKeys.size(), headersKeys.empty() ? NULL : &headersKeys[0], headersValues.empty() ? NULL : &headersValues[0]);
+        &status, payloadBuffer.GetObject(), redirectionBuffer.GetObject(), uri, ip,
+        headersKeys.size(), headersKeys.empty() ? NULL : &headersKeys[0], headersValues.empty() ? NULL : &headersValues[0],
+        getKeys.size(), getKeys.empty() ? NULL : &getKeys[0], getValues.empty() ? NULL : &getValues[0]);
 
       if (code != OrthancPluginErrorCode_Success)
       {
@@ -6907,13 +6908,16 @@
       {
         switch (status)
         {
-        case OrthancPluginHttpAuthenticationStatus_Success:
+        case OrthancPluginHttpAuthenticationStatus_Granted:
           payloadBuffer.MoveToString(customPayload);
-          return IIncomingHttpRequestFilter::AuthenticationStatus_Success;
+          return IIncomingHttpRequestFilter::AuthenticationStatus_Granted;
 
         case OrthancPluginHttpAuthenticationStatus_Unauthorized:
           return IIncomingHttpRequestFilter::AuthenticationStatus_Unauthorized;
 
+        case OrthancPluginHttpAuthenticationStatus_Forbidden:
+          return IIncomingHttpRequestFilter::AuthenticationStatus_Forbidden;
+
         case OrthancPluginHttpAuthenticationStatus_Redirect:
           redirectionBuffer.MoveToString(redirection);
           return IIncomingHttpRequestFilter::AuthenticationStatus_Redirect;
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.h	Fri Jul 11 13:58:27 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.h	Fri Jul 11 16:34:33 2025 +0200
@@ -423,9 +423,10 @@
     IIncomingHttpRequestFilter::AuthenticationStatus CheckAuthentication(
       std::string& customPayload,
       std::string& redirection,
-      const std::string& uri,
-      const HttpToolbox::GetArguments& getArguments,
-      const HttpToolbox::Arguments& httpHeaders) const;
+      const char* uri,
+      const char* ip,
+      const HttpToolbox::Arguments& httpHeaders,
+      const HttpToolbox::GetArguments& getArguments) const;
   };
 }
 
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Fri Jul 11 13:58:27 2025 +0200
+++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Fri Jul 11 16:34:33 2025 +0200
@@ -1208,9 +1208,10 @@
    **/
   typedef enum
   {
-    OrthancPluginHttpAuthenticationStatus_Success = 0,       /*!< The authentication has succeeded */
+    OrthancPluginHttpAuthenticationStatus_Granted = 0,       /*!< The authentication has been granted */
     OrthancPluginHttpAuthenticationStatus_Unauthorized = 1,  /*!< The authentication has failed (401 HTTP status) */
-    OrthancPluginHttpAuthenticationStatus_Redirect = 2,      /*!< Redirect to another path (e.g. for login, 307 HTTP status) */
+    OrthancPluginHttpAuthenticationStatus_Forbidden = 2,     /*!< The authorization has failed (403 HTTP status) */
+    OrthancPluginHttpAuthenticationStatus_Redirect = 3,      /*!< Redirect to another path (e.g. for login, 307 HTTP status) */
 
     _OrthancPluginHttpAuthenticationStatus_INTERNAL = 0x7fffffff
   } OrthancPluginHttpAuthenticationStatus;
@@ -10380,23 +10381,24 @@
 
 
   /**
-   * @brief Callback to authenticate a HTTP request
+   * @brief Callback to authenticate a HTTP request.
    *
    * Signature of a callback function that authenticates every incoming HTTP.
    *
    * @param status The output status of the authentication.
-   * @param customPayload If status is `OrthancPluginHttpAuthenticationStatus_Success`,
+   * @param customPayload If status is `OrthancPluginHttpAuthenticationStatus_Granted`,
    * a custom payload that will be provided to the HTTP authorization callback.
    * @param redirection If status is `OrthancPluginHttpAuthenticationStatus_Redirect`,
    * a buffer filled with the path where to redirect the user (typically, a login page).
    * The path is relative to the root of the Web server of Orthanc.
    * @param uri The URI of interest (without the possible GET arguments).
+   * @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.
    * @param getCount For a GET request, the number of GET parameters.
    * @param getKeys For a GET request, the keys of the GET parameters.
    * @param getValues For a GET request, the values of the GET parameters.
-   * @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 success, other value if error.
    * @ingroup Callbacks
    **/
@@ -10405,12 +10407,13 @@
     OrthancPluginMemoryBuffer*              customPayload,  /* out */
     OrthancPluginMemoryBuffer*              redirection,    /* out */
     const char*                             uri,
+    const char*                             ip,
+    uint32_t                                headersCount,
+    const char* const*                      headersKeys,
+    const char* const*                      headersValues,
     uint32_t                                getCount,
     const char* const*                      getKeys,
-    const char* const*                      getValues,
-    uint32_t                                headersCount,
-    const char* const*                      headersKeys,
-    const char* const*                      headersValues);
+    const char* const*                      getValues);
 
 
   typedef struct
@@ -10419,21 +10422,29 @@
   } _OrthancPluginHttpAuthentication;
 
   /**
-   * @brief Register a callback to handle HTTP authentication.
+   * @brief Register a callback to handle HTTP authentication (and
+   * possibly HTTP authorization).
    *
    * This function installs a callback that is executed for each
    * incoming HTTP request to handle HTTP authentication. At most one
    * plugin can register such a callback. This gives the opportunity
-   * to one plugin to validate access tokens (such as a JWT), possibly
+   * to the plugin to validate access tokens (such as a JWT), possibly
    * redirecting the user to a login page. The authentication callback
    * can generate a custom payload that will be provided to the
    * subsequent REST handling callback.
    *
-   * The HTTP authentication callback can notably be used if some
+   * This HTTP authentication callback can notably be used if some
    * resource in the REST API must be available for public access, as
    * soon as the "RemoteAccessAllowed" configuration option is set to
    * "true".
    *
+   * In addition, the callback can handle HTTP authorization
+   * simultaneously with HTTP authentication, by reporting the
+   * "OrthancPluginHttpAuthenticationStatus_Forbidden" status. This
+   * corresponds to callbacks installed using
+   * OrthancPluginRegisterIncomingHttpRequestFilter2(), but the latter
+   * callbacks do not provide access to the authentication payload.
+   *
    * If one plugin installs a HTTP authentication callback, the
    * built-in HTTP authentication of Orthanc is disabled. This means
    * that the "RegisteredUsers" and "AuthenticationEnabled"
--- a/OrthancServer/Sources/main.cpp	Fri Jul 11 13:58:27 2025 +0200
+++ b/OrthancServer/Sources/main.cpp	Fri Jul 11 16:34:33 2025 +0200
@@ -605,20 +605,21 @@
     return true;
   }
 
-  virtual AuthenticationStatus CheckAuthentication(std::string& customPayload,
-                                                   std::string& redirection,
-                                                   const std::string& uri,
-                                                   const HttpToolbox::GetArguments& getArguments,
-                                                   const HttpToolbox::Arguments& httpHeaders) const ORTHANC_OVERRIDE
+  virtual AuthenticationStatus CheckAuthentication(std::string& customPayload /* out: payload to provide to "IsAllowed()" */,
+                                                   std::string& redirection   /* out: path relative to the root */,
+                                                   const char* uri,
+                                                   const char* ip,
+                                                   const HttpToolbox::Arguments& httpHeaders,
+                                                   const HttpToolbox::GetArguments& getArguments) const ORTHANC_OVERRIDE
   {
 #if ORTHANC_ENABLE_PLUGINS == 1
     if (plugins_ != NULL)
     {
-      return plugins_->CheckAuthentication(customPayload, redirection, uri, getArguments, httpHeaders);
+      return plugins_->CheckAuthentication(customPayload, redirection, uri, ip, httpHeaders, getArguments);
     }
 #endif
 
-    return AuthenticationStatus_NotImplemented;
+    return AuthenticationStatus_BuiltIn;
   }
 };