0
|
1 /**
|
|
2 * Google Cloud Platform credentials for DICOMweb and Orthanc
|
|
3 * Copyright (C) 2019 Osimis S.A., Belgium
|
|
4 *
|
|
5 * This program is free software: you can redistribute it and/or
|
|
6 * modify it under the terms of the GNU General Public License as
|
|
7 * published by the Free Software Foundation, either version 3 of the
|
|
8 * License, or (at your option) any later version.
|
|
9 *
|
|
10 * In addition, as a special exception, the copyright holders of this
|
|
11 * program give permission to link the code of its release with the
|
|
12 * OpenSSL project's "OpenSSL" library (or with modified versions of it
|
|
13 * that use the same license as the "OpenSSL" library), and distribute
|
|
14 * the linked executables. You must obey the GNU General Public License
|
|
15 * in all respects for all of the code used other than "OpenSSL". If you
|
|
16 * modify file(s) with this exception, you may extend this exception to
|
|
17 * your version of the file(s), but you are not obligated to do so. If
|
|
18 * you do not wish to do so, delete this exception statement from your
|
|
19 * version. If you delete this exception statement from all source files
|
|
20 * in the program, then also delete it here.
|
|
21 *
|
|
22 * This program is distributed in the hope that it will be useful, but
|
|
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
25 * General Public License for more details.
|
|
26 *
|
|
27 * You should have received a copy of the GNU General Public License
|
|
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
29 **/
|
|
30
|
|
31
|
|
32 #include "GoogleUpdater.h"
|
|
33
|
|
34 #include "GoogleConfiguration.h"
|
|
35
|
|
36 #include <google/cloud/storage/internal/curl_handle_factory.h>
|
|
37 #include <google/cloud/storage/oauth2/google_credentials.h>
|
|
38
|
|
39
|
|
40 namespace
|
|
41 {
|
|
42 class CurlBuilder : public google::cloud::storage::internal::CurlRequestBuilder
|
|
43 {
|
|
44 private:
|
|
45 class HandleFactory : public google::cloud::storage::internal::DefaultCurlHandleFactory
|
|
46 {
|
|
47 public:
|
|
48 google::cloud::storage::internal::CurlPtr CreateHandle() override
|
|
49 {
|
|
50 google::cloud::storage::internal::CurlPtr handle
|
|
51 (google::cloud::storage::internal::DefaultCurlHandleFactory::CreateHandle());
|
|
52
|
|
53 const GoogleConfiguration& configuration = GoogleConfiguration::GetInstance();
|
|
54
|
|
55 long timeout = static_cast<long>(configuration.GetTimeoutSeconds());
|
|
56
|
|
57 if ((!configuration.GetCaInfo().empty() &&
|
|
58 curl_easy_setopt(handle.get(), CURLOPT_CAINFO, configuration.GetCaInfo().c_str()) != CURLE_OK) ||
|
|
59 curl_easy_setopt(handle.get(), CURLOPT_SSL_VERIFYHOST, 2) != CURLE_OK ||
|
|
60 curl_easy_setopt(handle.get(), CURLOPT_SSL_VERIFYPEER, 1) != CURLE_OK ||
|
|
61 curl_easy_setopt(handle.get(), CURLOPT_TIMEOUT, timeout) != CURLE_OK)
|
|
62 {
|
|
63 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
|
|
64 "Cannot initialize a libcurl handle");
|
|
65 }
|
|
66
|
|
67 return handle;
|
|
68 }
|
|
69
|
|
70 google::cloud::storage::internal::CurlMulti CreateMultiHandle() override
|
|
71 {
|
|
72 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
|
|
73 }
|
|
74 };
|
|
75
|
|
76 public:
|
|
77 CurlBuilder(std::string base_url,
|
|
78 std::shared_ptr<google::cloud::storage::internal::CurlHandleFactory> factory) :
|
|
79 CurlRequestBuilder(base_url, std::make_shared<HandleFactory>())
|
|
80 {
|
|
81 }
|
|
82 };
|
|
83 }
|
|
84
|
|
85
|
|
86
|
|
87 static boost::posix_time::ptime GetNow()
|
|
88 {
|
|
89 return boost::posix_time::second_clock::local_time();
|
|
90 }
|
|
91
|
|
92
|
|
93 void GoogleUpdater::Worker(const State* state,
|
|
94 const GoogleAccount* account,
|
|
95 long refreshIntervalSeconds)
|
|
96 {
|
|
97 std::shared_ptr<google::cloud::storage::oauth2::Credentials> credentials;
|
|
98
|
|
99 switch (account->GetType())
|
|
100 {
|
|
101 case GoogleAccount::Type_ServiceAccount:
|
|
102 credentials = std::make_shared<google::cloud::storage::oauth2::ServiceAccountCredentials
|
|
103 <CurlBuilder>>(account->GetServiceAccount());
|
|
104 break;
|
|
105
|
|
106 case GoogleAccount::Type_AuthorizedUser:
|
|
107 credentials = std::make_shared<google::cloud::storage::oauth2::AuthorizedUserCredentials
|
|
108 <CurlBuilder>>(account->GetAuthorizedUser());
|
|
109 break;
|
|
110
|
|
111 default:
|
|
112 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
|
|
113 }
|
|
114
|
|
115 if (credentials.get() == NULL)
|
|
116 {
|
|
117 LOG(ERROR) << "Cannot initialize the token updater for Google Cloud Platform account: "
|
|
118 << account->GetName();
|
|
119 return;
|
|
120 }
|
|
121
|
|
122 const std::string dicomWebPluginRoot = GoogleConfiguration::GetInstance().GetDicomWebPluginRoot();
|
|
123 const std::string baseGoogleUrl = GoogleConfiguration::GetInstance().GetBaseGoogleUrl();
|
|
124
|
|
125 std::string lastToken;
|
|
126 std::unique_ptr<boost::posix_time::ptime> lastUpdate;
|
|
127
|
|
128 while (*state == State_Running)
|
|
129 {
|
|
130 if (lastUpdate.get() == NULL ||
|
|
131 (GetNow() - *lastUpdate).total_seconds() >= refreshIntervalSeconds)
|
|
132 {
|
|
133 google::cloud::StatusOr<std::string> token = credentials->AuthorizationHeader();
|
|
134 if (!token)
|
|
135 {
|
|
136 LOG(WARNING) << "Cannot generate Google Cloud Platform token for account: " << account->GetName();
|
|
137 }
|
|
138 else if (*token != lastToken &&
|
|
139 account->UpdateServerDefinition(dicomWebPluginRoot, baseGoogleUrl, *token))
|
|
140 {
|
|
141 lastToken = *token;
|
|
142 }
|
|
143
|
|
144 lastUpdate.reset(new boost::posix_time::ptime(GetNow()));
|
|
145 }
|
|
146
|
|
147 boost::this_thread::sleep(boost::posix_time::milliseconds(100));
|
|
148 }
|
|
149 }
|
|
150
|
|
151
|
|
152 GoogleUpdater::~GoogleUpdater()
|
|
153 {
|
|
154 if (state_ == State_Running)
|
|
155 {
|
|
156 LOG(ERROR) << "GoogleUpdater::Stop() should have been manually called";
|
|
157 Stop();
|
|
158 }
|
|
159 }
|
|
160
|
|
161
|
|
162 void GoogleUpdater::Start()
|
|
163 {
|
|
164 if (state_ != State_Setup)
|
|
165 {
|
|
166 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
|
|
167 }
|
|
168
|
|
169 state_ = State_Running;
|
|
170
|
|
171 const GoogleConfiguration& configuration = GoogleConfiguration::GetInstance();
|
|
172
|
|
173 workers_.resize(configuration.GetAccountsCount());
|
|
174
|
|
175 for (size_t i = 0; i < workers_.size(); i++)
|
|
176 {
|
|
177 workers_[i] = new boost::thread(Worker, &state_, &configuration.GetAccount(i),
|
|
178 configuration.GetRefreshIntervalSeconds());
|
|
179 }
|
|
180 }
|
|
181
|
|
182
|
|
183 void GoogleUpdater::Stop()
|
|
184 {
|
|
185 if (state_ == State_Running)
|
|
186 {
|
|
187 state_ = State_Done;
|
|
188
|
|
189 for (size_t i = 0; i < workers_.size(); i++)
|
|
190 {
|
|
191 if (workers_[i] != NULL)
|
|
192 {
|
|
193 if (workers_[i]->joinable())
|
|
194 {
|
|
195 workers_[i]->join();
|
|
196 }
|
|
197
|
|
198 delete workers_[i];
|
|
199 }
|
|
200 }
|
|
201
|
|
202 workers_.clear();
|
|
203 }
|
|
204 }
|