Mercurial > hg > orthanc
comparison 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 |
comparison
equal
deleted
inserted
replaced
4043:6c6239aec462 | 4044:d25f4c0fa160 |
---|---|
1 /** | |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium | |
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 "PrecompiledHeaders.h" | |
35 #include "WebServiceParameters.h" | |
36 | |
37 #include "Logging.h" | |
38 #include "OrthancException.h" | |
39 #include "SerializationToolbox.h" | |
40 #include "Toolbox.h" | |
41 | |
42 #if ORTHANC_SANDBOXED == 0 | |
43 # include "SystemToolbox.h" | |
44 #endif | |
45 | |
46 #include <cassert> | |
47 | |
48 namespace Orthanc | |
49 { | |
50 static const char* KEY_CERTIFICATE_FILE = "CertificateFile"; | |
51 static const char* KEY_CERTIFICATE_KEY_FILE = "CertificateKeyFile"; | |
52 static const char* KEY_CERTIFICATE_KEY_PASSWORD = "CertificateKeyPassword"; | |
53 static const char* KEY_HTTP_HEADERS = "HttpHeaders"; | |
54 static const char* KEY_PASSWORD = "Password"; | |
55 static const char* KEY_PKCS11 = "Pkcs11"; | |
56 static const char* KEY_URL = "Url"; | |
57 static const char* KEY_URL_2 = "URL"; | |
58 static const char* KEY_USERNAME = "Username"; | |
59 | |
60 | |
61 static bool IsReservedKey(const std::string& key) | |
62 { | |
63 return (key == KEY_CERTIFICATE_FILE || | |
64 key == KEY_CERTIFICATE_KEY_FILE || | |
65 key == KEY_CERTIFICATE_KEY_PASSWORD || | |
66 key == KEY_HTTP_HEADERS || | |
67 key == KEY_PASSWORD || | |
68 key == KEY_PKCS11 || | |
69 key == KEY_URL || | |
70 key == KEY_URL_2 || | |
71 key == KEY_USERNAME); | |
72 } | |
73 | |
74 | |
75 WebServiceParameters::WebServiceParameters() : | |
76 pkcs11Enabled_(false) | |
77 { | |
78 SetUrl("http://127.0.0.1:8042/"); | |
79 } | |
80 | |
81 | |
82 void WebServiceParameters::ClearClientCertificate() | |
83 { | |
84 certificateFile_.clear(); | |
85 certificateKeyFile_.clear(); | |
86 certificateKeyPassword_.clear(); | |
87 } | |
88 | |
89 | |
90 void WebServiceParameters::SetUrl(const std::string& url) | |
91 { | |
92 if (!Toolbox::StartsWith(url, "http://") && | |
93 !Toolbox::StartsWith(url, "https://")) | |
94 { | |
95 throw OrthancException(ErrorCode_BadFileFormat, "Bad URL: " + url); | |
96 } | |
97 | |
98 // Add trailing slash if needed | |
99 if (url[url.size() - 1] == '/') | |
100 { | |
101 url_ = url; | |
102 } | |
103 else | |
104 { | |
105 url_ = url + '/'; | |
106 } | |
107 } | |
108 | |
109 | |
110 void WebServiceParameters::ClearCredentials() | |
111 { | |
112 username_.clear(); | |
113 password_.clear(); | |
114 } | |
115 | |
116 | |
117 void WebServiceParameters::SetCredentials(const std::string& username, | |
118 const std::string& password) | |
119 { | |
120 if (username.empty() && | |
121 !password.empty()) | |
122 { | |
123 throw OrthancException(ErrorCode_BadFileFormat); | |
124 } | |
125 else | |
126 { | |
127 username_ = username; | |
128 password_ = password; | |
129 } | |
130 } | |
131 | |
132 | |
133 void WebServiceParameters::SetClientCertificate(const std::string& certificateFile, | |
134 const std::string& certificateKeyFile, | |
135 const std::string& certificateKeyPassword) | |
136 { | |
137 if (certificateFile.empty()) | |
138 { | |
139 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
140 } | |
141 | |
142 if (certificateKeyPassword.empty()) | |
143 { | |
144 throw OrthancException( | |
145 ErrorCode_BadFileFormat, | |
146 "The password for the HTTPS certificate is not provided: " + certificateFile); | |
147 } | |
148 | |
149 certificateFile_ = certificateFile; | |
150 certificateKeyFile_ = certificateKeyFile; | |
151 certificateKeyPassword_ = certificateKeyPassword; | |
152 } | |
153 | |
154 | |
155 void WebServiceParameters::FromSimpleFormat(const Json::Value& peer) | |
156 { | |
157 assert(peer.isArray()); | |
158 | |
159 pkcs11Enabled_ = false; | |
160 ClearClientCertificate(); | |
161 | |
162 if (peer.size() != 1 && | |
163 peer.size() != 3) | |
164 { | |
165 throw OrthancException(ErrorCode_BadFileFormat); | |
166 } | |
167 | |
168 SetUrl(peer.get(0u, "").asString()); | |
169 | |
170 if (peer.size() == 1) | |
171 { | |
172 ClearCredentials(); | |
173 } | |
174 else if (peer.size() == 2) | |
175 { | |
176 throw OrthancException(ErrorCode_BadFileFormat, | |
177 "The HTTP password is not provided"); | |
178 } | |
179 else if (peer.size() == 3) | |
180 { | |
181 SetCredentials(peer.get(1u, "").asString(), | |
182 peer.get(2u, "").asString()); | |
183 } | |
184 else | |
185 { | |
186 throw OrthancException(ErrorCode_BadFileFormat); | |
187 } | |
188 } | |
189 | |
190 | |
191 static std::string GetStringMember(const Json::Value& peer, | |
192 const std::string& key, | |
193 const std::string& defaultValue) | |
194 { | |
195 if (!peer.isMember(key)) | |
196 { | |
197 return defaultValue; | |
198 } | |
199 else if (peer[key].type() != Json::stringValue) | |
200 { | |
201 throw OrthancException(ErrorCode_BadFileFormat); | |
202 } | |
203 else | |
204 { | |
205 return peer[key].asString(); | |
206 } | |
207 } | |
208 | |
209 | |
210 void WebServiceParameters::FromAdvancedFormat(const Json::Value& peer) | |
211 { | |
212 assert(peer.isObject()); | |
213 | |
214 std::string url = GetStringMember(peer, KEY_URL, ""); | |
215 if (url.empty()) | |
216 { | |
217 SetUrl(GetStringMember(peer, KEY_URL_2, "")); | |
218 } | |
219 else | |
220 { | |
221 SetUrl(url); | |
222 } | |
223 | |
224 SetCredentials(GetStringMember(peer, KEY_USERNAME, ""), | |
225 GetStringMember(peer, KEY_PASSWORD, "")); | |
226 | |
227 std::string file = GetStringMember(peer, KEY_CERTIFICATE_FILE, ""); | |
228 if (!file.empty()) | |
229 { | |
230 SetClientCertificate(file, GetStringMember(peer, KEY_CERTIFICATE_KEY_FILE, ""), | |
231 GetStringMember(peer, KEY_CERTIFICATE_KEY_PASSWORD, "")); | |
232 } | |
233 else | |
234 { | |
235 ClearClientCertificate(); | |
236 } | |
237 | |
238 if (peer.isMember(KEY_PKCS11)) | |
239 { | |
240 if (peer[KEY_PKCS11].type() == Json::booleanValue) | |
241 { | |
242 pkcs11Enabled_ = peer[KEY_PKCS11].asBool(); | |
243 } | |
244 else | |
245 { | |
246 throw OrthancException(ErrorCode_BadFileFormat); | |
247 } | |
248 } | |
249 else | |
250 { | |
251 pkcs11Enabled_ = false; | |
252 } | |
253 | |
254 | |
255 headers_.clear(); | |
256 | |
257 if (peer.isMember(KEY_HTTP_HEADERS)) | |
258 { | |
259 const Json::Value& h = peer[KEY_HTTP_HEADERS]; | |
260 if (h.type() != Json::objectValue) | |
261 { | |
262 throw OrthancException(ErrorCode_BadFileFormat); | |
263 } | |
264 else | |
265 { | |
266 Json::Value::Members keys = h.getMemberNames(); | |
267 for (size_t i = 0; i < keys.size(); i++) | |
268 { | |
269 const Json::Value& value = h[keys[i]]; | |
270 if (value.type() != Json::stringValue) | |
271 { | |
272 throw OrthancException(ErrorCode_BadFileFormat); | |
273 } | |
274 else | |
275 { | |
276 headers_[keys[i]] = value.asString(); | |
277 } | |
278 } | |
279 } | |
280 } | |
281 | |
282 | |
283 userProperties_.clear(); | |
284 | |
285 const Json::Value::Members members = peer.getMemberNames(); | |
286 | |
287 for (Json::Value::Members::const_iterator it = members.begin(); | |
288 it != members.end(); ++it) | |
289 { | |
290 if (!IsReservedKey(*it)) | |
291 { | |
292 switch (peer[*it].type()) | |
293 { | |
294 case Json::stringValue: | |
295 userProperties_[*it] = peer[*it].asString(); | |
296 break; | |
297 | |
298 case Json::booleanValue: | |
299 userProperties_[*it] = peer[*it].asBool() ? "1" : "0"; | |
300 break; | |
301 | |
302 case Json::intValue: | |
303 userProperties_[*it] = boost::lexical_cast<std::string>(peer[*it].asInt()); | |
304 break; | |
305 | |
306 default: | |
307 throw OrthancException(ErrorCode_BadFileFormat, | |
308 "User-defined properties associated with a Web service must be strings: " + *it); | |
309 } | |
310 } | |
311 } | |
312 } | |
313 | |
314 | |
315 void WebServiceParameters::Unserialize(const Json::Value& peer) | |
316 { | |
317 try | |
318 { | |
319 if (peer.isArray()) | |
320 { | |
321 FromSimpleFormat(peer); | |
322 } | |
323 else if (peer.isObject()) | |
324 { | |
325 FromAdvancedFormat(peer); | |
326 } | |
327 else | |
328 { | |
329 throw OrthancException(ErrorCode_BadFileFormat); | |
330 } | |
331 } | |
332 catch (OrthancException&) | |
333 { | |
334 throw; | |
335 } | |
336 catch (...) | |
337 { | |
338 throw OrthancException(ErrorCode_BadFileFormat); | |
339 } | |
340 } | |
341 | |
342 | |
343 void WebServiceParameters::ListHttpHeaders(std::set<std::string>& target) const | |
344 { | |
345 target.clear(); | |
346 | |
347 for (Dictionary::const_iterator it = headers_.begin(); | |
348 it != headers_.end(); ++it) | |
349 { | |
350 target.insert(it->first); | |
351 } | |
352 } | |
353 | |
354 | |
355 bool WebServiceParameters::LookupHttpHeader(std::string& value, | |
356 const std::string& key) const | |
357 { | |
358 Dictionary::const_iterator found = headers_.find(key); | |
359 | |
360 if (found == headers_.end()) | |
361 { | |
362 return false; | |
363 } | |
364 else | |
365 { | |
366 value = found->second; | |
367 return true; | |
368 } | |
369 } | |
370 | |
371 | |
372 void WebServiceParameters::AddUserProperty(const std::string& key, | |
373 const std::string& value) | |
374 { | |
375 if (IsReservedKey(key)) | |
376 { | |
377 throw OrthancException( | |
378 ErrorCode_ParameterOutOfRange, | |
379 "Cannot use this reserved key to name an user property: " + key); | |
380 } | |
381 else | |
382 { | |
383 userProperties_[key] = value; | |
384 } | |
385 } | |
386 | |
387 | |
388 void WebServiceParameters::ListUserProperties(std::set<std::string>& target) const | |
389 { | |
390 target.clear(); | |
391 | |
392 for (Dictionary::const_iterator it = userProperties_.begin(); | |
393 it != userProperties_.end(); ++it) | |
394 { | |
395 target.insert(it->first); | |
396 } | |
397 } | |
398 | |
399 | |
400 bool WebServiceParameters::LookupUserProperty(std::string& value, | |
401 const std::string& key) const | |
402 { | |
403 Dictionary::const_iterator found = userProperties_.find(key); | |
404 | |
405 if (found == userProperties_.end()) | |
406 { | |
407 return false; | |
408 } | |
409 else | |
410 { | |
411 value = found->second; | |
412 return true; | |
413 } | |
414 } | |
415 | |
416 | |
417 bool WebServiceParameters::GetBooleanUserProperty(const std::string& key, | |
418 bool defaultValue) const | |
419 { | |
420 Dictionary::const_iterator found = userProperties_.find(key); | |
421 | |
422 if (found == userProperties_.end()) | |
423 { | |
424 return defaultValue; | |
425 } | |
426 else if (found->second == "0" || | |
427 found->second == "false") | |
428 { | |
429 return false; | |
430 } | |
431 else if (found->second == "1" || | |
432 found->second == "true") | |
433 { | |
434 return true; | |
435 } | |
436 else | |
437 { | |
438 throw OrthancException(ErrorCode_BadFileFormat, "Bad value for a Boolean user property in the parameters " | |
439 "of a Web service: Property \"" + key + "\" equals: " + found->second); | |
440 } | |
441 } | |
442 | |
443 | |
444 bool WebServiceParameters::IsAdvancedFormatNeeded() const | |
445 { | |
446 return (!certificateFile_.empty() || | |
447 !certificateKeyFile_.empty() || | |
448 !certificateKeyPassword_.empty() || | |
449 pkcs11Enabled_ || | |
450 !headers_.empty() || | |
451 !userProperties_.empty()); | |
452 } | |
453 | |
454 | |
455 void WebServiceParameters::Serialize(Json::Value& value, | |
456 bool forceAdvancedFormat, | |
457 bool includePasswords) const | |
458 { | |
459 if (forceAdvancedFormat || | |
460 IsAdvancedFormatNeeded()) | |
461 { | |
462 value = Json::objectValue; | |
463 value[KEY_URL] = url_; | |
464 | |
465 if (!username_.empty() || | |
466 !password_.empty()) | |
467 { | |
468 value[KEY_USERNAME] = username_; | |
469 | |
470 if (includePasswords) | |
471 { | |
472 value[KEY_PASSWORD] = password_; | |
473 } | |
474 } | |
475 | |
476 if (!certificateFile_.empty()) | |
477 { | |
478 value[KEY_CERTIFICATE_FILE] = certificateFile_; | |
479 } | |
480 | |
481 if (!certificateKeyFile_.empty()) | |
482 { | |
483 value[KEY_CERTIFICATE_KEY_FILE] = certificateKeyFile_; | |
484 } | |
485 | |
486 if (!certificateKeyPassword_.empty() && | |
487 includePasswords) | |
488 { | |
489 value[KEY_CERTIFICATE_KEY_PASSWORD] = certificateKeyPassword_; | |
490 } | |
491 | |
492 value[KEY_PKCS11] = pkcs11Enabled_; | |
493 | |
494 value[KEY_HTTP_HEADERS] = Json::objectValue; | |
495 for (Dictionary::const_iterator it = headers_.begin(); | |
496 it != headers_.end(); ++it) | |
497 { | |
498 value[KEY_HTTP_HEADERS][it->first] = it->second; | |
499 } | |
500 | |
501 for (Dictionary::const_iterator it = userProperties_.begin(); | |
502 it != userProperties_.end(); ++it) | |
503 { | |
504 value[it->first] = it->second; | |
505 } | |
506 } | |
507 else | |
508 { | |
509 value = Json::arrayValue; | |
510 value.append(url_); | |
511 | |
512 if (!username_.empty() || | |
513 !password_.empty()) | |
514 { | |
515 value.append(username_); | |
516 value.append(includePasswords ? password_ : ""); | |
517 } | |
518 } | |
519 } | |
520 | |
521 | |
522 #if ORTHANC_SANDBOXED == 0 | |
523 void WebServiceParameters::CheckClientCertificate() const | |
524 { | |
525 if (!certificateFile_.empty()) | |
526 { | |
527 if (!SystemToolbox::IsRegularFile(certificateFile_)) | |
528 { | |
529 throw OrthancException(ErrorCode_InexistentFile, | |
530 "Cannot open certificate file: " + certificateFile_); | |
531 } | |
532 | |
533 if (!certificateKeyFile_.empty() && | |
534 !SystemToolbox::IsRegularFile(certificateKeyFile_)) | |
535 { | |
536 throw OrthancException(ErrorCode_InexistentFile, | |
537 "Cannot open key file: " + certificateKeyFile_); | |
538 } | |
539 } | |
540 } | |
541 #endif | |
542 | |
543 | |
544 void WebServiceParameters::FormatPublic(Json::Value& target) const | |
545 { | |
546 target = Json::objectValue; | |
547 | |
548 // Only return the public information identifying the destination. | |
549 // "Security"-related information such as passwords and HTTP | |
550 // headers are shown as "null" values. | |
551 target[KEY_URL] = url_; | |
552 | |
553 if (!username_.empty()) | |
554 { | |
555 target[KEY_USERNAME] = username_; | |
556 target[KEY_PASSWORD] = Json::nullValue; | |
557 } | |
558 | |
559 if (!certificateFile_.empty()) | |
560 { | |
561 target[KEY_CERTIFICATE_FILE] = certificateFile_; | |
562 target[KEY_CERTIFICATE_KEY_FILE] = Json::nullValue; | |
563 target[KEY_CERTIFICATE_KEY_PASSWORD] = Json::nullValue; | |
564 } | |
565 | |
566 target[KEY_PKCS11] = pkcs11Enabled_; | |
567 | |
568 Json::Value headers = Json::arrayValue; | |
569 | |
570 for (Dictionary::const_iterator it = headers_.begin(); | |
571 it != headers_.end(); ++it) | |
572 { | |
573 // Only list the HTTP headers, not their value | |
574 headers.append(it->first); | |
575 } | |
576 | |
577 target[KEY_HTTP_HEADERS] = headers; | |
578 | |
579 for (Dictionary::const_iterator it = userProperties_.begin(); | |
580 it != userProperties_.end(); ++it) | |
581 { | |
582 target[it->first] = it->second; | |
583 } | |
584 } | |
585 } |