diff Plugin/GoogleUpdater.cpp @ 0:520cba9a0d42

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 13 Jun 2019 14:57:22 +0200
parents
children 25292488ff8f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugin/GoogleUpdater.cpp	Thu Jun 13 14:57:22 2019 +0200
@@ -0,0 +1,204 @@
+/**
+ * Google Cloud Platform credentials for DICOMweb and Orthanc
+ * Copyright (C) 2019 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 "GoogleUpdater.h"
+
+#include "GoogleConfiguration.h"
+
+#include <google/cloud/storage/internal/curl_handle_factory.h>
+#include <google/cloud/storage/oauth2/google_credentials.h>
+
+
+namespace
+{
+  class CurlBuilder : public google::cloud::storage::internal::CurlRequestBuilder
+  {
+  private:
+    class HandleFactory : public google::cloud::storage::internal::DefaultCurlHandleFactory
+    {
+    public:
+      google::cloud::storage::internal::CurlPtr CreateHandle() override
+      {
+        google::cloud::storage::internal::CurlPtr handle
+          (google::cloud::storage::internal::DefaultCurlHandleFactory::CreateHandle());
+
+        const GoogleConfiguration& configuration = GoogleConfiguration::GetInstance();
+
+        long timeout = static_cast<long>(configuration.GetTimeoutSeconds());
+
+        if ((!configuration.GetCaInfo().empty() &&
+             curl_easy_setopt(handle.get(), CURLOPT_CAINFO, configuration.GetCaInfo().c_str()) != CURLE_OK) ||
+            curl_easy_setopt(handle.get(), CURLOPT_SSL_VERIFYHOST, 2) != CURLE_OK ||
+            curl_easy_setopt(handle.get(), CURLOPT_SSL_VERIFYPEER, 1) != CURLE_OK ||
+            curl_easy_setopt(handle.get(), CURLOPT_TIMEOUT, timeout) != CURLE_OK)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
+                                          "Cannot initialize a libcurl handle");
+        }
+
+        return handle;
+      }
+
+      google::cloud::storage::internal::CurlMulti CreateMultiHandle() override
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+    };
+
+  public:
+    CurlBuilder(std::string base_url,
+                std::shared_ptr<google::cloud::storage::internal::CurlHandleFactory> factory) :
+      CurlRequestBuilder(base_url, std::make_shared<HandleFactory>())
+    {
+    }
+  };
+}
+
+
+
+static boost::posix_time::ptime GetNow()
+{
+  return boost::posix_time::second_clock::local_time();
+} 
+
+
+void GoogleUpdater::Worker(const State* state,
+                           const GoogleAccount* account,
+                           long refreshIntervalSeconds)
+{
+  std::shared_ptr<google::cloud::storage::oauth2::Credentials> credentials;
+
+  switch (account->GetType())
+  {
+    case GoogleAccount::Type_ServiceAccount:
+      credentials = std::make_shared<google::cloud::storage::oauth2::ServiceAccountCredentials
+        <CurlBuilder>>(account->GetServiceAccount());
+      break;
+
+    case GoogleAccount::Type_AuthorizedUser:
+      credentials = std::make_shared<google::cloud::storage::oauth2::AuthorizedUserCredentials
+        <CurlBuilder>>(account->GetAuthorizedUser());
+      break;
+
+    default:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+  }
+
+  if (credentials.get() == NULL)
+  {
+    LOG(ERROR) << "Cannot initialize the token updater for Google Cloud Platform account: " 
+               << account->GetName();
+    return;
+  }
+
+  const std::string dicomWebPluginRoot = GoogleConfiguration::GetInstance().GetDicomWebPluginRoot();
+  const std::string baseGoogleUrl = GoogleConfiguration::GetInstance().GetBaseGoogleUrl();
+
+  std::string lastToken;
+  std::unique_ptr<boost::posix_time::ptime> lastUpdate;
+
+  while (*state == State_Running)
+  {
+    if (lastUpdate.get() == NULL ||
+        (GetNow() - *lastUpdate).total_seconds() >= refreshIntervalSeconds)
+    {
+      google::cloud::StatusOr<std::string> token = credentials->AuthorizationHeader();
+      if (!token)
+      {
+        LOG(WARNING) << "Cannot generate Google Cloud Platform token for account: " << account->GetName();
+      }
+      else if (*token != lastToken &&
+               account->UpdateServerDefinition(dicomWebPluginRoot, baseGoogleUrl, *token))
+      {
+        lastToken = *token;
+      }
+
+      lastUpdate.reset(new boost::posix_time::ptime(GetNow()));
+    }
+      
+    boost::this_thread::sleep(boost::posix_time::milliseconds(100));
+  }
+}
+
+
+GoogleUpdater::~GoogleUpdater()
+{
+  if (state_ == State_Running)
+  {
+    LOG(ERROR) << "GoogleUpdater::Stop() should have been manually called";
+    Stop();
+  }
+}
+
+
+void GoogleUpdater::Start()
+{
+  if (state_ != State_Setup)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+  }
+
+  state_ = State_Running;
+
+  const GoogleConfiguration& configuration = GoogleConfiguration::GetInstance();
+
+  workers_.resize(configuration.GetAccountsCount());
+
+  for (size_t i = 0; i < workers_.size(); i++)
+  {
+    workers_[i] = new boost::thread(Worker, &state_, &configuration.GetAccount(i), 
+                                    configuration.GetRefreshIntervalSeconds());
+  }
+}
+
+  
+void GoogleUpdater::Stop()
+{
+  if (state_ == State_Running)
+  {
+    state_ = State_Done;
+
+    for (size_t i = 0; i < workers_.size(); i++)
+    {
+      if (workers_[i] != NULL)
+      {
+        if (workers_[i]->joinable())
+        {
+          workers_[i]->join();
+        }
+
+        delete workers_[i];
+      }
+    }
+
+    workers_.clear();
+  }
+}