diff OrthancFramework/Sources/WebServiceParameters.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents Core/WebServiceParameters.cpp@ae0e3fd609df
children 3dda0d73193c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancFramework/Sources/WebServiceParameters.cpp	Wed Jun 10 20:30:34 2020 +0200
@@ -0,0 +1,585 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 Osimis S.A., 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/>.
+ **/
+
+
+#include "PrecompiledHeaders.h"
+#include "WebServiceParameters.h"
+
+#include "Logging.h"
+#include "OrthancException.h"
+#include "SerializationToolbox.h"
+#include "Toolbox.h"
+
+#if ORTHANC_SANDBOXED == 0
+#  include "SystemToolbox.h"
+#endif
+
+#include <cassert>
+
+namespace Orthanc
+{
+  static const char* KEY_CERTIFICATE_FILE = "CertificateFile";
+  static const char* KEY_CERTIFICATE_KEY_FILE = "CertificateKeyFile";
+  static const char* KEY_CERTIFICATE_KEY_PASSWORD = "CertificateKeyPassword";
+  static const char* KEY_HTTP_HEADERS = "HttpHeaders";
+  static const char* KEY_PASSWORD = "Password";
+  static const char* KEY_PKCS11 = "Pkcs11";
+  static const char* KEY_URL = "Url";
+  static const char* KEY_URL_2 = "URL";
+  static const char* KEY_USERNAME = "Username";
+
+
+  static bool IsReservedKey(const std::string& key)
+  {
+    return (key == KEY_CERTIFICATE_FILE ||
+            key == KEY_CERTIFICATE_KEY_FILE ||
+            key == KEY_CERTIFICATE_KEY_PASSWORD ||
+            key == KEY_HTTP_HEADERS ||
+            key == KEY_PASSWORD ||
+            key == KEY_PKCS11 ||
+            key == KEY_URL ||
+            key == KEY_URL_2 ||
+            key == KEY_USERNAME);
+  }
+
+
+  WebServiceParameters::WebServiceParameters() : 
+    pkcs11Enabled_(false)
+  {
+    SetUrl("http://127.0.0.1:8042/");
+  }
+
+
+  void WebServiceParameters::ClearClientCertificate()
+  {
+    certificateFile_.clear();
+    certificateKeyFile_.clear();
+    certificateKeyPassword_.clear();
+  }
+
+
+  void WebServiceParameters::SetUrl(const std::string& url)
+  {
+    if (!Toolbox::StartsWith(url, "http://") &&
+        !Toolbox::StartsWith(url, "https://"))
+    {
+      throw OrthancException(ErrorCode_BadFileFormat, "Bad URL: " + url);
+    }
+
+    // Add trailing slash if needed
+    if (url[url.size() - 1] == '/')
+    {
+      url_ = url;
+    }
+    else
+    {
+      url_ = url + '/';
+    }
+  }
+
+
+  void WebServiceParameters::ClearCredentials()
+  {
+    username_.clear();
+    password_.clear();
+  }
+
+
+  void WebServiceParameters::SetCredentials(const std::string& username,
+                                            const std::string& password)
+  {
+    if (username.empty() && 
+        !password.empty())
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+    else
+    {
+      username_ = username;
+      password_ = password;
+    }
+  }
+
+
+  void WebServiceParameters::SetClientCertificate(const std::string& certificateFile,
+                                                  const std::string& certificateKeyFile,
+                                                  const std::string& certificateKeyPassword)
+  {
+    if (certificateFile.empty())
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    if (certificateKeyPassword.empty())
+    {
+      throw OrthancException(
+        ErrorCode_BadFileFormat,
+        "The password for the HTTPS certificate is not provided: " + certificateFile);
+    }
+
+    certificateFile_ = certificateFile;
+    certificateKeyFile_ = certificateKeyFile;
+    certificateKeyPassword_ = certificateKeyPassword;
+  }
+
+
+  void WebServiceParameters::FromSimpleFormat(const Json::Value& peer)
+  {
+    assert(peer.isArray());
+
+    pkcs11Enabled_ = false;
+    ClearClientCertificate();
+
+    if (peer.size() != 1 && 
+        peer.size() != 3)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    SetUrl(peer.get(0u, "").asString());
+
+    if (peer.size() == 1)
+    {
+      ClearCredentials();
+    }
+    else if (peer.size() == 2)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat,
+                             "The HTTP password is not provided");
+    }
+    else if (peer.size() == 3)
+    {
+      SetCredentials(peer.get(1u, "").asString(),
+                     peer.get(2u, "").asString());
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+
+  static std::string GetStringMember(const Json::Value& peer,
+                                     const std::string& key,
+                                     const std::string& defaultValue)
+  {
+    if (!peer.isMember(key))
+    {
+      return defaultValue;
+    }
+    else if (peer[key].type() != Json::stringValue)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+    else
+    {
+      return peer[key].asString();
+    }
+  }
+
+
+  void WebServiceParameters::FromAdvancedFormat(const Json::Value& peer)
+  {
+    assert(peer.isObject());
+
+    std::string url = GetStringMember(peer, KEY_URL, "");
+    if (url.empty())
+    {
+      SetUrl(GetStringMember(peer, KEY_URL_2, ""));
+    }
+    else
+    {
+      SetUrl(url);
+    }
+
+    SetCredentials(GetStringMember(peer, KEY_USERNAME, ""),
+                   GetStringMember(peer, KEY_PASSWORD, ""));
+
+    std::string file = GetStringMember(peer, KEY_CERTIFICATE_FILE, "");
+    if (!file.empty())
+    {
+      SetClientCertificate(file, GetStringMember(peer, KEY_CERTIFICATE_KEY_FILE, ""),
+                           GetStringMember(peer, KEY_CERTIFICATE_KEY_PASSWORD, ""));
+    }
+    else
+    {
+      ClearClientCertificate();
+    }
+
+    if (peer.isMember(KEY_PKCS11))
+    {
+      if (peer[KEY_PKCS11].type() == Json::booleanValue)
+      {
+        pkcs11Enabled_ = peer[KEY_PKCS11].asBool();
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+    }
+    else
+    {
+      pkcs11Enabled_ = false;
+    }
+
+
+    headers_.clear();
+
+    if (peer.isMember(KEY_HTTP_HEADERS))
+    {
+      const Json::Value& h = peer[KEY_HTTP_HEADERS];
+      if (h.type() != Json::objectValue)
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+      else
+      {
+        Json::Value::Members keys = h.getMemberNames();
+        for (size_t i = 0; i < keys.size(); i++)
+        {
+          const Json::Value& value = h[keys[i]];
+          if (value.type() != Json::stringValue)
+          {
+            throw OrthancException(ErrorCode_BadFileFormat);
+          }
+          else
+          {
+            headers_[keys[i]] = value.asString();
+          }
+        }
+      }
+    }
+
+
+    userProperties_.clear();
+
+    const Json::Value::Members members = peer.getMemberNames();
+
+    for (Json::Value::Members::const_iterator it = members.begin(); 
+         it != members.end(); ++it)
+    {
+      if (!IsReservedKey(*it))
+      {
+        switch (peer[*it].type())
+        {
+          case Json::stringValue:
+            userProperties_[*it] = peer[*it].asString();
+            break;
+
+          case Json::booleanValue:
+            userProperties_[*it] = peer[*it].asBool() ? "1" : "0";
+            break;
+
+          case Json::intValue:
+            userProperties_[*it] = boost::lexical_cast<std::string>(peer[*it].asInt());
+            break;
+
+          default:
+            throw OrthancException(ErrorCode_BadFileFormat,
+                                   "User-defined properties associated with a Web service must be strings: " + *it);
+        }
+      }
+    }
+  }
+
+
+  void WebServiceParameters::Unserialize(const Json::Value& peer)
+  {
+    try
+    {
+      if (peer.isArray())
+      {
+        FromSimpleFormat(peer);
+      }
+      else if (peer.isObject())
+      {
+        FromAdvancedFormat(peer);
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+    }
+    catch (OrthancException&)
+    {
+      throw;
+    }
+    catch (...)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+
+  void WebServiceParameters::ListHttpHeaders(std::set<std::string>& target) const
+  {
+    target.clear();
+
+    for (Dictionary::const_iterator it = headers_.begin();
+         it != headers_.end(); ++it)
+    {
+      target.insert(it->first);
+    }
+  }
+
+
+  bool WebServiceParameters::LookupHttpHeader(std::string& value,
+                                              const std::string& key) const
+  {
+    Dictionary::const_iterator found = headers_.find(key);
+
+    if (found == headers_.end())
+    {
+      return false;
+    }
+    else
+    {
+      value = found->second;
+      return true;
+    }
+  }
+
+
+  void WebServiceParameters::AddUserProperty(const std::string& key,
+                                             const std::string& value)
+  {
+    if (IsReservedKey(key))
+    {
+      throw OrthancException(
+        ErrorCode_ParameterOutOfRange,
+        "Cannot use this reserved key to name an user property: " + key);
+    }
+    else
+    {
+      userProperties_[key] = value;
+    }
+  }
+
+
+  void WebServiceParameters::ListUserProperties(std::set<std::string>& target) const
+  {
+    target.clear();
+
+    for (Dictionary::const_iterator it = userProperties_.begin();
+         it != userProperties_.end(); ++it)
+    {
+      target.insert(it->first);
+    }
+  }
+
+
+  bool WebServiceParameters::LookupUserProperty(std::string& value,
+                                                const std::string& key) const
+  {
+    Dictionary::const_iterator found = userProperties_.find(key);
+
+    if (found == userProperties_.end())
+    {
+      return false;
+    }
+    else
+    {
+      value = found->second;
+      return true;
+    }
+  }
+  
+
+  bool WebServiceParameters::GetBooleanUserProperty(const std::string& key,
+                                                    bool defaultValue) const
+  {
+    Dictionary::const_iterator found = userProperties_.find(key);
+
+    if (found == userProperties_.end())
+    {
+      return defaultValue;
+    }
+    else if (found->second == "0" ||
+             found->second == "false")
+    {
+      return false;
+    }
+    else if (found->second == "1" ||
+             found->second == "true")
+    {
+      return true;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_BadFileFormat, "Bad value for a Boolean user property in the parameters "
+                             "of a Web service: Property \"" + key + "\" equals: " + found->second);
+    }    
+  }
+
+
+  bool WebServiceParameters::IsAdvancedFormatNeeded() const
+  {
+    return (!certificateFile_.empty() ||
+            !certificateKeyFile_.empty() ||
+            !certificateKeyPassword_.empty() ||
+            pkcs11Enabled_ ||
+            !headers_.empty() ||
+            !userProperties_.empty());
+  }
+
+
+  void WebServiceParameters::Serialize(Json::Value& value,
+                                       bool forceAdvancedFormat,
+                                       bool includePasswords) const
+  {
+    if (forceAdvancedFormat ||
+        IsAdvancedFormatNeeded())
+    {
+      value = Json::objectValue;
+      value[KEY_URL] = url_;
+
+      if (!username_.empty() ||
+          !password_.empty())
+      {
+        value[KEY_USERNAME] = username_;
+
+        if (includePasswords)
+        {
+          value[KEY_PASSWORD] = password_;
+        }
+      }
+
+      if (!certificateFile_.empty())
+      {
+        value[KEY_CERTIFICATE_FILE] = certificateFile_;
+      }
+
+      if (!certificateKeyFile_.empty())
+      {
+        value[KEY_CERTIFICATE_KEY_FILE] = certificateKeyFile_;
+      }
+
+      if (!certificateKeyPassword_.empty() &&
+          includePasswords)
+      {
+        value[KEY_CERTIFICATE_KEY_PASSWORD] = certificateKeyPassword_;
+      }
+
+      value[KEY_PKCS11] = pkcs11Enabled_;
+
+      value[KEY_HTTP_HEADERS] = Json::objectValue;
+      for (Dictionary::const_iterator it = headers_.begin();
+           it != headers_.end(); ++it)
+      {
+        value[KEY_HTTP_HEADERS][it->first] = it->second;
+      }
+
+      for (Dictionary::const_iterator it = userProperties_.begin();
+           it != userProperties_.end(); ++it)
+      {
+        value[it->first] = it->second;
+      }
+    }
+    else
+    {
+      value = Json::arrayValue;
+      value.append(url_);
+
+      if (!username_.empty() ||
+          !password_.empty())
+      {
+        value.append(username_);
+        value.append(includePasswords ? password_ : "");
+      }
+    }
+  }
+
+
+#if ORTHANC_SANDBOXED == 0
+  void WebServiceParameters::CheckClientCertificate() const
+  {
+    if (!certificateFile_.empty())
+    {
+      if (!SystemToolbox::IsRegularFile(certificateFile_))
+      {
+        throw OrthancException(ErrorCode_InexistentFile,
+                               "Cannot open certificate file: " + certificateFile_);
+      }
+
+      if (!certificateKeyFile_.empty() && 
+          !SystemToolbox::IsRegularFile(certificateKeyFile_))
+      {
+        throw OrthancException(ErrorCode_InexistentFile,
+                               "Cannot open key file: " + certificateKeyFile_);
+      }
+    }
+  }
+#endif
+
+
+  void WebServiceParameters::FormatPublic(Json::Value& target) const
+  {
+    target = Json::objectValue;
+
+    // Only return the public information identifying the destination.
+    // "Security"-related information such as passwords and HTTP
+    // headers are shown as "null" values.
+    target[KEY_URL] = url_;
+
+    if (!username_.empty())
+    {
+      target[KEY_USERNAME] = username_;
+      target[KEY_PASSWORD] = Json::nullValue;
+    }
+
+    if (!certificateFile_.empty())
+    {
+      target[KEY_CERTIFICATE_FILE] = certificateFile_;
+      target[KEY_CERTIFICATE_KEY_FILE] = Json::nullValue;
+      target[KEY_CERTIFICATE_KEY_PASSWORD] = Json::nullValue;      
+    }
+
+    target[KEY_PKCS11] = pkcs11Enabled_;
+
+    Json::Value headers = Json::arrayValue;
+      
+    for (Dictionary::const_iterator it = headers_.begin();
+         it != headers_.end(); ++it)
+    {
+      // Only list the HTTP headers, not their value
+      headers.append(it->first);
+    }
+
+    target[KEY_HTTP_HEADERS] = headers;
+
+    for (Dictionary::const_iterator it = userProperties_.begin();
+         it != userProperties_.end(); ++it)
+    {
+      target[it->first] = it->second;
+    }
+  }
+}