Mercurial > hg > orthanc-tests
annotate Tests/CheckDicomTls.py @ 662:b31aa560210d
note
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 04 Jul 2024 18:29:08 +0200 |
parents | 31a7e52b3da6 |
children |
rev | line source |
---|---|
610
ec657d1a62a6
fix compatibility with python3
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
588
diff
changeset
|
1 #!/usr/bin/python3 |
375 | 2 |
3 # Orthanc - A Lightweight, RESTful DICOM Store | |
4 # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
5 # Department, University Hospital of Liege, Belgium | |
649
5d7b6e43ab7d
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
640
diff
changeset
|
6 # Copyright (C) 2017-2023 Osimis S.A., Belgium |
5d7b6e43ab7d
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
640
diff
changeset
|
7 # Copyright (C) 2024-2024 Orthanc Team SRL, Belgium |
640
9f8276ac1cdd
update year to 2024
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
610
diff
changeset
|
8 # Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium |
375 | 9 # |
10 # This program is free software: you can redistribute it and/or | |
11 # modify it under the terms of the GNU General Public License as | |
12 # published by the Free Software Foundation, either version 3 of the | |
13 # License, or (at your option) any later version. | |
14 # | |
15 # This program is distributed in the hope that it will be useful, but | |
16 # WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 # General Public License for more details. | |
19 # | |
20 # You should have received a copy of the GNU General Public License | |
21 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
22 | |
23 | |
24 | |
25 import argparse | |
26 import os | |
27 import pprint | |
28 import re | |
29 import sys | |
30 import subprocess | |
31 import unittest | |
32 | |
33 from Toolbox import * | |
34 | |
35 | |
36 ## | |
37 ## Parse the command-line arguments | |
38 ## | |
39 | |
40 parser = argparse.ArgumentParser(description = 'Run the integration tests for DICOM TLS in Orthanc.') | |
41 | |
42 parser.add_argument('--server', | |
43 default = 'localhost', | |
44 help = 'Address of the Orthanc server to test') | |
45 parser.add_argument('--aet', | |
46 default = 'ORTHANC', | |
47 help = 'AET of the Orthanc instance to test') | |
48 parser.add_argument('--dicom', | |
49 type = int, | |
50 default = 4242, | |
51 help = 'DICOM port of the Orthanc instance to test') | |
52 parser.add_argument('--rest', | |
53 type = int, | |
54 default = 8042, | |
55 help = 'Port to the REST API') | |
56 parser.add_argument('--username', | |
57 default = 'alice', | |
58 help = 'Username to the REST API') | |
59 parser.add_argument('--password', | |
60 default = 'orthanctest', | |
61 help = 'Password to the REST API') | |
62 parser.add_argument('--force', help = 'Do not warn the user', | |
63 action = 'store_true') | |
658
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
64 parser.add_argument('--config-no-check-client', help = 'Create the configuration files for the "no-check-client" tests in the current folder', |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
65 action = 'store_true') |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
66 parser.add_argument('--config-check-client', help = 'Create the configuration files for the "check-client" tests test in the current folder', |
375 | 67 action = 'store_true') |
68 parser.add_argument('options', metavar = 'N', nargs = '*', | |
69 help='Arguments to Python unittest') | |
70 | |
71 args = parser.parse_args() | |
72 | |
73 | |
74 ## | |
75 ## Configure the testing context | |
76 ## | |
77 | |
78 | |
658
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
79 if args.config_no_check_client or args.config_check_client: |
375 | 80 def CreateCertificate(name): |
81 subprocess.check_call([ 'openssl', 'req', '-x509', '-nodes', '-days', '365', '-newkey', 'rsa:2048', | |
82 '-keyout', '%s.key' % name, | |
83 '-out', '%s.crt' % name, | |
84 '-subj', '/C=BE/CN=localhost' ]) | |
85 | |
658
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
86 print('Writing configuration for the %s tests to current folder' % ('no-check-client' if args.config_no_check_client else 'check-client')) |
375 | 87 CreateCertificate('dicom-tls-a') |
88 CreateCertificate('dicom-tls-b') | |
89 CreateCertificate('dicom-tls-c') # Not trusted by Orthanc | |
90 | |
91 with open('dicom-tls-trusted.crt', 'w') as f: | |
92 for i in [ 'dicom-tls-a.crt', 'dicom-tls-b.crt' ]: | |
93 with open(i, 'r') as g: | |
94 f.write(g.read()) | |
95 | |
96 with open('dicom-tls.json', 'w') as f: | |
97 f.write(json.dumps({ | |
98 'DicomTlsEnabled' : True, | |
99 'DicomTlsCertificate' : 'dicom-tls-a.crt', | |
100 'DicomTlsPrivateKey' : 'dicom-tls-a.key', | |
101 'DicomTlsTrustedCertificates' : 'dicom-tls-trusted.crt', | |
102 'ExecuteLuaEnabled' : True, | |
103 'RemoteAccessAllowed' : True, | |
104 'RegisteredUsers' : { | |
105 'alice' : 'orthanctest' | |
106 }, | |
658
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
107 'DicomTlsRemoteCertificateRequired' : args.config_check_client, # New in Orthanc 1.9.3 |
375 | 108 })) |
109 | |
110 exit(0) | |
111 | |
112 | |
113 if not args.force: | |
114 print(""" | |
115 WARNING: This test will remove all the content of your | |
116 Orthanc instance running on %s! | |
117 | |
118 Are you sure ["yes" to go on]?""" % args.server) | |
119 | |
120 if sys.stdin.readline().strip() != 'yes': | |
121 print('Aborting...') | |
122 exit(0) | |
123 | |
124 | |
125 ORTHANC = DefineOrthanc(server = args.server, | |
126 username = args.username, | |
127 password = args.password, | |
128 restPort = args.rest, | |
129 aet = args.aet, | |
130 dicomPort = args.dicom) | |
131 | |
132 | |
133 ## | |
134 ## The tests | |
135 ## | |
136 | |
137 | |
138 FNULL = open(os.devnull, 'w') # Emulates "subprocess.DEVNULL" on Python 2.7 | |
139 | |
140 | |
658
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
141 # in these tests, Orthanc does not check client certificates |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
142 class OrthancNoCheckClient(unittest.TestCase): |
375 | 143 def setUp(self): |
144 if (sys.version_info >= (3, 0)): | |
145 # Remove annoying warnings about unclosed socket in Python 3 | |
146 import warnings | |
147 warnings.simplefilter('ignore', ResourceWarning) | |
148 | |
149 DropOrthanc(ORTHANC) | |
150 | |
151 | |
152 def test_incoming(self): | |
658
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
153 # No client certificate provided and client does not check server cert -> raise |
375 | 154 self.assertRaises(Exception, lambda: subprocess.check_call([ |
155 FindExecutable('echoscu'), | |
156 ORTHANC['Server'], | |
157 str(ORTHANC['DicomPort']), | |
158 '-aec', 'ORTHANC', | |
159 ], stderr = FNULL)) | |
160 | |
658
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
161 # No client certificate provided and client does check server cert -> no raise |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
162 self.assertRaises(Exception, lambda: subprocess.check_call([ |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
163 FindExecutable('echoscu'), |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
164 ORTHANC['Server'], |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
165 str(ORTHANC['DicomPort']), |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
166 '-aec', 'ORTHANC', |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
167 '+cf', 'dicom-tls-a.crt' |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
168 ], stderr = FNULL)) |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
169 |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
170 # random client certificate provided and client does check server cert -> no raise since Orthanc does not check the client cert |
375 | 171 subprocess.check_call([ |
172 FindExecutable('echoscu'), | |
173 ORTHANC['Server'], | |
174 str(ORTHANC['DicomPort']), | |
175 '-aec', 'ORTHANC', | |
176 '+tls', 'dicom-tls-b.key', 'dicom-tls-b.crt', | |
177 '+cf', 'dicom-tls-a.crt', | |
178 ], stderr = FNULL) | |
179 | |
180 | |
181 | |
182 def test_outgoing_to_self(self): | |
183 u = UploadInstance(ORTHANC, 'DummyCT.dcm') ['ID'] | |
184 | |
185 # Error, as DICOM TLS is not enabled | |
186 DoPut(ORTHANC, '/modalities/self', { | |
187 'AET' : 'ORTHANC', | |
188 'Host' : ORTHANC['Server'], | |
189 'Port' : ORTHANC['DicomPort'], | |
190 }) | |
191 | |
192 self.assertRaises(Exception, lambda: DoPost(ORTHANC, '/modalities/self/store', u)) | |
193 | |
194 # Retry using DICOM TLS | |
195 DoPut(ORTHANC, '/modalities/self', { | |
196 'AET' : 'ORTHANC', | |
197 'Host' : ORTHANC['Server'], | |
198 'Port' : ORTHANC['DicomPort'], | |
199 'UseDicomTls' : True, | |
200 }) | |
201 | |
202 self.assertEqual(1, DoPost(ORTHANC, '/modalities/self/store', u) ['InstancesCount']) | |
203 | |
400
f454fe86061b
dicom tls: test_anonymous
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
204 |
f454fe86061b
dicom tls: test_anonymous
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
205 def test_anonymous(self): |
f454fe86061b
dicom tls: test_anonymous
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
206 # Fails on Orthanc <= 1.9.2 |
588
8aa101e126d0
migration to UCLouvain servers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
511
diff
changeset
|
207 # https://orthanc.uclouvain.be/book/faq/dicom-tls.html#secure-tls-connections-without-certificate |
400
f454fe86061b
dicom tls: test_anonymous
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
208 subprocess.check_call([ |
f454fe86061b
dicom tls: test_anonymous
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
209 FindExecutable('echoscu'), |
f454fe86061b
dicom tls: test_anonymous
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
210 ORTHANC['Server'], |
f454fe86061b
dicom tls: test_anonymous
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
211 str(ORTHANC['DicomPort']), |
f454fe86061b
dicom tls: test_anonymous
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
212 '-aec', 'ORTHANC', |
f454fe86061b
dicom tls: test_anonymous
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
213 '--anonymous-tls', |
f454fe86061b
dicom tls: test_anonymous
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
214 '+cf', 'dicom-tls-a.crt', |
f454fe86061b
dicom tls: test_anonymous
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
215 ], stderr = FNULL) |
f454fe86061b
dicom tls: test_anonymous
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
375
diff
changeset
|
216 |
658
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
217 |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
218 # in these tests, Orthanc do checks client certificates |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
219 class OrthancCheckClient(unittest.TestCase): |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
220 def setUp(self): |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
221 if (sys.version_info >= (3, 0)): |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
222 # Remove annoying warnings about unclosed socket in Python 3 |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
223 import warnings |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
224 warnings.simplefilter('ignore', ResourceWarning) |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
225 |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
226 DropOrthanc(ORTHANC) |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
227 |
375 | 228 |
658
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
229 def test_check_client_incoming(self): |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
230 # client provides an untrusted certificate -> Orthanc will complain -> raise |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
231 self.assertRaises(Exception, lambda: subprocess.check_call([ |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
232 FindExecutable('echoscu'), |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
233 ORTHANC['Server'], |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
234 str(ORTHANC['DicomPort']), |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
235 '-aec', 'ORTHANC', |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
236 '+tls', 'dicom-tls-c.key', 'dicom-tls-c.crt', # Not trusted by Orthanc |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
237 '+cf', 'dicom-tls-a.crt', |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
238 ], stderr = FNULL)) |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
239 |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
240 # client provides a trusted certificate but expects another cert from Orthanc -> raise |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
241 self.assertRaises(Exception, lambda: subprocess.check_call([ |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
242 FindExecutable('echoscu'), |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
243 ORTHANC['Server'], |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
244 str(ORTHANC['DicomPort']), |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
245 '-aec', 'ORTHANC', |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
246 '+tls', 'dicom-tls-b.key', 'dicom-tls-b.crt', |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
247 '+cf', 'dicom-tls-b.crt', # Not the certificate of Orthanc |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
248 ], stderr = FNULL)) |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
249 |
31a7e52b3da6
split DICOM TLS in 2: check-client and no-check-client
Alain Mazy <am@orthanc.team>
parents:
649
diff
changeset
|
250 |
375 | 251 try: |
252 print('\nStarting the tests...') | |
253 unittest.main(argv = [ sys.argv[0] ] + args.options) | |
254 | |
255 finally: | |
256 print('\nDone') |