Mercurial > hg > orthanc-gcp
annotate Plugin/GoogleAccount.cpp @ 54:509334672b6b default tip
updated copyright, as Orthanc Team now replaces Osimis
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 30 May 2024 22:48:01 +0200 |
parents | 21499c134785 |
children |
rev | line source |
---|---|
0 | 1 /** |
2 * Google Cloud Platform credentials for DICOMweb and Orthanc | |
54
509334672b6b
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
42
diff
changeset
|
3 * Copyright (C) 2019-2023 Osimis S.A., Belgium |
509334672b6b
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
42
diff
changeset
|
4 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium |
509334672b6b
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
42
diff
changeset
|
5 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium |
0 | 6 * |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU General Public License as | |
9 * published by the Free Software Foundation, either version 3 of the | |
10 * License, or (at your option) any later version. | |
11 * | |
12 * In addition, as a special exception, the copyright holders of this | |
13 * program give permission to link the code of its release with the | |
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
15 * that use the same license as the "OpenSSL" library), and distribute | |
16 * the linked executables. You must obey the GNU General Public License | |
17 * in all respects for all of the code used other than "OpenSSL". If you | |
18 * modify file(s) with this exception, you may extend this exception to | |
19 * your version of the file(s), but you are not obligated to do so. If | |
20 * you do not wish to do so, delete this exception statement from your | |
21 * version. If you delete this exception statement from all source files | |
22 * in the program, then also delete it here. | |
23 * | |
24 * This program is distributed in the hope that it will be useful, but | |
25 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
27 * General Public License for more details. | |
28 * | |
29 * You should have received a copy of the GNU General Public License | |
30 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
31 **/ | |
32 | |
33 | |
34 #include "GoogleAccount.h" | |
35 | |
40 | 36 #include <Logging.h> |
31 | 37 #include <Toolbox.h> |
0 | 38 |
39 void GoogleAccount::LoadAuthorizedUser(const std::string& json) | |
40 { | |
41 google::cloud::StatusOr<google::cloud::storage::oauth2::AuthorizedUserCredentialsInfo> info = | |
42 google::cloud::storage::oauth2::ParseAuthorizedUserCredentials(json, "memory"); | |
43 | |
44 if (!info) | |
45 { | |
46 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
47 "Cannot parse authorized user configuration"); | |
48 } | |
49 else | |
50 { | |
51 type_ = Type_AuthorizedUser; | |
52 authorizedUser_.reset(new google::cloud::storage::oauth2::AuthorizedUserCredentialsInfo(*info)); | |
53 } | |
54 } | |
55 | |
56 | |
57 bool GoogleAccount::LoadServiceAccount(const OrthancPlugins::OrthancConfiguration& account) | |
58 { | |
59 std::string path; | |
60 | |
61 if (!account.LookupStringValue(path, "ServiceAccountFile")) | |
62 { | |
63 return false; | |
64 } | |
65 | |
66 OrthancPlugins::MemoryBuffer f; | |
67 f.ReadFile(path); | |
68 | |
69 std::string s; | |
70 f.ToString(s); | |
71 | |
72 google::cloud::StatusOr<google::cloud::storage::oauth2::ServiceAccountCredentialsInfo> info = | |
73 google::cloud::storage::oauth2::ParseServiceAccountCredentials(s, "memory"); | |
74 | |
75 if (!info) | |
76 { | |
77 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
78 "Cannot parse service account configuration at: " + path); | |
79 } | |
80 | |
81 type_ = Type_ServiceAccount; | |
82 serviceAccount_.reset(new google::cloud::storage::oauth2::ServiceAccountCredentialsInfo(*info)); | |
83 return true; | |
84 } | |
85 | |
86 | |
87 bool GoogleAccount::LoadAuthorizedUserFile(const OrthancPlugins::OrthancConfiguration& account) | |
88 { | |
89 std::string path; | |
90 | |
91 if (account.LookupStringValue(path, "AuthorizedUserFile")) | |
92 { | |
93 OrthancPlugins::MemoryBuffer f; | |
94 f.ReadFile(path); | |
95 | |
96 std::string s; | |
97 f.ToString(s); | |
98 | |
99 LoadAuthorizedUser(s); | |
100 return true; | |
101 } | |
102 else | |
103 { | |
104 return false; | |
105 } | |
106 } | |
107 | |
108 | |
109 bool GoogleAccount::LoadAuthorizedUserStrings(const OrthancPlugins::OrthancConfiguration& account) | |
110 { | |
111 std::string clientId, clientSecret, refreshToken; | |
112 | |
113 if (account.LookupStringValue(clientId, "AuthorizedUserClientId") && | |
114 account.LookupStringValue(clientSecret, "AuthorizedUserClientSecret") && | |
115 account.LookupStringValue(refreshToken, "AuthorizedUserRefreshToken")) | |
116 { | |
117 Json::Value json = Json::objectValue; | |
118 json["client_id"] = clientId; | |
119 json["client_secret"] = clientSecret; | |
120 json["refresh_token"] = refreshToken; | |
121 | |
122 LoadAuthorizedUser(json.toStyledString()); | |
123 return true; | |
124 } | |
125 else | |
126 { | |
127 return false; | |
128 } | |
129 } | |
130 | |
131 | |
132 GoogleAccount::GoogleAccount(const OrthancPlugins::OrthancConfiguration& account, | |
133 const std::string& name) : | |
134 name_(name) | |
135 { | |
136 if (!account.LookupStringValue(project_, "Project")) | |
137 { | |
138 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
139 "Missing \"Project\" option for account \"" + name + "\""); | |
140 } | |
141 | |
142 if (!account.LookupStringValue(location_, "Location")) | |
143 { | |
144 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
145 "Missing \"Location\" option for account \"" + name + "\""); | |
146 } | |
147 | |
148 if (!account.LookupStringValue(dataset_, "Dataset")) | |
149 { | |
150 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
151 "Missing \"Dataset\" option for account \"" + name + "\""); | |
152 } | |
153 | |
154 if (!account.LookupStringValue(dicomStore_, "DicomStore")) | |
155 { | |
156 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, | |
157 "Missing \"DicomStore\" option for account \"" + name + "\""); | |
158 } | |
159 | |
160 if (!LoadServiceAccount(account) && | |
161 !LoadAuthorizedUserFile(account) && | |
162 !LoadAuthorizedUserStrings(account)) | |
163 { | |
164 throw Orthanc::OrthancException( | |
165 Orthanc::ErrorCode_BadFileFormat, | |
166 "Missing \"ServiceAccount\" or \"AuthorizedUserXXX\" option for account \"" + name + "\""); | |
167 } | |
168 } | |
169 | |
170 | |
171 const google::cloud::storage::oauth2::AuthorizedUserCredentialsInfo& GoogleAccount::GetAuthorizedUser() const | |
172 { | |
173 if (authorizedUser_.get() == NULL) | |
174 { | |
175 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
176 } | |
177 else | |
178 { | |
179 return *authorizedUser_; | |
180 } | |
181 } | |
182 | |
183 | |
184 google::cloud::storage::oauth2::ServiceAccountCredentialsInfo& GoogleAccount::GetServiceAccount() const | |
185 { | |
186 if (serviceAccount_.get() == NULL) | |
187 { | |
188 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
189 } | |
190 else | |
191 { | |
192 return *serviceAccount_; | |
193 } | |
194 } | |
195 | |
196 | |
197 static std::string AddTrailingSlash(const std::string& url) | |
198 { | |
199 // Add a trailing slash if needed | |
200 if (url.empty() || | |
201 url[url.size() - 1] != '/') | |
202 { | |
203 return url + '/'; | |
204 } | |
205 else | |
206 { | |
207 return url; | |
208 } | |
209 } | |
210 | |
211 | |
212 bool GoogleAccount::UpdateServerDefinition(const std::string& dicomWebPluginRoot, | |
213 const std::string& baseGoogleUrl, | |
214 const std::string& token) const | |
215 { | |
216 std::string url = (AddTrailingSlash(baseGoogleUrl) + | |
217 "projects/" + project_ + | |
218 "/locations/" + location_ + | |
219 "/datasets/" + dataset_ + | |
220 "/dicomStores/" + dicomStore_ + | |
221 "/dicomWeb/"); | |
222 | |
11
798951e457f3
fix parsing of authorization header
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
223 size_t colon = token.find(':'); |
798951e457f3
fix parsing of authorization header
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
224 if (colon == std::string::npos) |
798951e457f3
fix parsing of authorization header
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
225 { |
798951e457f3
fix parsing of authorization header
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
226 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); |
798951e457f3
fix parsing of authorization header
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
227 } |
798951e457f3
fix parsing of authorization header
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
228 |
798951e457f3
fix parsing of authorization header
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
229 std::string headerKey = Orthanc::Toolbox::StripSpaces(token.substr(0, colon)); |
798951e457f3
fix parsing of authorization header
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
230 std::string headerValue = Orthanc::Toolbox::StripSpaces(token.substr(colon + 1)); |
798951e457f3
fix parsing of authorization header
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
231 |
0 | 232 Json::Value headers = Json::objectValue; |
11
798951e457f3
fix parsing of authorization header
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
233 headers[headerKey] = headerValue; |
0 | 234 |
235 Json::Value server = Json::objectValue; | |
236 server["Url"] = url; | |
237 server["HasDelete"] = "1"; // Google Cloud Platform allows "-X DELETE" | |
238 server["HttpHeaders"] = headers; | |
239 | |
240 Json::Value answer; | |
241 if (OrthancPlugins::RestApiPut(answer, AddTrailingSlash(dicomWebPluginRoot) + "servers/" + name_, | |
242 server, true)) | |
243 { | |
244 return true; | |
245 } | |
246 else | |
247 { | |
248 LOG(ERROR) << "Cannot update DICOMweb access to Google Cloud Platform: " << name_; | |
249 return false; | |
250 } | |
251 } |