comparison UnitTestsSources/UnitTestsMain.cpp @ 77:94a9484d7f8f

fix security issues allowing to browse remote dicom servers + introduced UnitTests
author Alain Mazy <am@osimis.io>
date Wed, 15 Mar 2023 16:36:42 +0100
parents
children 0ffad746a16b
comparison
equal deleted inserted replaced
76:d301047ee3c4 77:94a9484d7f8f
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-2021 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 Affero General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20
21
22 #include <gtest/gtest.h>
23 #include <boost/lexical_cast.hpp>
24 #include <boost/algorithm/string/predicate.hpp>
25
26 #include "../Plugin/DefaultAuthorizationParser.h"
27 #include "../Plugin/AssociativeArray.h"
28 #include "../Plugin/AccessedResource.h"
29 #include "../Plugin/MemoryCache.h"
30 #include "../Plugin/PermissionParser.h"
31 #include "../Plugin/ResourceHierarchyCache.h"
32
33 using namespace OrthancPlugins;
34
35 std::string instanceOrthancId = "44444444-44444444-44444444-44444444-44444444";
36 std::string seriesOrthancId = "33333333-33333333-33333333-33333333-33333333";
37 std::string studyOrthancId = "22222222-22222222-22222222-22222222-22222222";
38 std::string patientOrthancId = "11111111-11111111-11111111-11111111-11111111";
39
40 std::string instanceDicomUid = "4.4";
41 std::string seriesDicomUid = "3.3";
42 std::string studyDicomUid = "2.2";
43 std::string patientDicomUid = "PATIENT.1";
44
45 bool IsAccessing(const IAuthorizationParser::AccessedResources& accesses, AccessLevel level, const std::string& orthancId)
46 {
47 for (IAuthorizationParser::AccessedResources::const_iterator it = accesses.begin(); it != accesses.end(); ++it)
48 {
49 if (it->GetLevel() == level && it->GetOrthancId() == orthancId)
50 {
51 return true;
52 }
53 }
54 return false;
55 }
56
57 namespace OrthancPlugins
58 {
59 // The namespace is necessary for friend classes to work
60 // http://code.google.com/p/googletest/wiki/AdvancedGuide#Private_Class_Members
61
62 TEST(DefaultAuthorizationParser, Parse)
63 {
64 MemoryCache::Factory factory(10);
65 DefaultAuthorizationParser parser(factory, "/dicom-web/");
66 ResourceHierarchyCache* cache = parser.GetResourceHierarchy();
67
68 cache->AddOrthancDicomMapping(Orthanc::ResourceType_Instance, instanceOrthancId, instanceDicomUid);
69 cache->AddOrthancDicomMapping(Orthanc::ResourceType_Series, seriesOrthancId, seriesDicomUid);
70 cache->AddOrthancDicomMapping(Orthanc::ResourceType_Study, studyOrthancId, studyDicomUid);
71 cache->AddOrthancDicomMapping(Orthanc::ResourceType_Patient, patientOrthancId, patientDicomUid);
72
73 cache->AddParentLink(Orthanc::ResourceType_Instance, instanceOrthancId, seriesOrthancId);
74 cache->AddParentLink(Orthanc::ResourceType_Series, seriesOrthancId, studyOrthancId);
75 cache->AddParentLink(Orthanc::ResourceType_Study, studyOrthancId, patientOrthancId);
76
77 IAuthorizationParser::AccessedResources accesses;
78 AssociativeArray noGetArguments(0, NULL, NULL, false);
79
80 accesses.clear();
81 parser.Parse(accesses, "/studies/22222222-22222222-22222222-22222222-22222222/", noGetArguments.GetMap());
82 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
83 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
84
85 accesses.clear();
86 parser.Parse(accesses, "/studies/22222222-22222222-22222222-22222222-22222222/instances", noGetArguments.GetMap());
87 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
88 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
89
90 accesses.clear();
91 parser.Parse(accesses, "/studies/22222222-22222222-22222222-22222222-22222222/archive", noGetArguments.GetMap());
92 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
93 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
94
95 accesses.clear();
96 parser.Parse(accesses, "/osimis-viewer/studies/22222222-22222222-22222222-22222222-22222222/archive", noGetArguments.GetMap());
97 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
98 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
99
100 accesses.clear();
101 parser.Parse(accesses, "/series/33333333-33333333-33333333-33333333-33333333/", noGetArguments.GetMap());
102 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
103 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
104 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
105
106 accesses.clear();
107 parser.Parse(accesses, "/series/33333333-33333333-33333333-33333333-33333333/media", noGetArguments.GetMap());
108 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
109 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
110 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
111
112 accesses.clear();
113 parser.Parse(accesses, "/series/33333333-33333333-33333333-33333333-33333333/modify", noGetArguments.GetMap());
114 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
115 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
116 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
117
118 accesses.clear();
119 parser.Parse(accesses, "/web-viewer/series/33333333-33333333-33333333-33333333-33333333", noGetArguments.GetMap());
120 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
121 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
122 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
123
124 accesses.clear();
125 parser.Parse(accesses, "/osimis-viewer/series/33333333-33333333-33333333-33333333-33333333", noGetArguments.GetMap());
126 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
127 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
128 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
129
130 accesses.clear();
131 parser.Parse(accesses, "/instances/44444444-44444444-44444444-44444444-44444444/file", noGetArguments.GetMap());
132 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Instance, instanceOrthancId));
133 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
134 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
135 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
136
137 accesses.clear();
138 parser.Parse(accesses, "/web-viewer/instances/jpeg95-44444444-44444444-44444444-44444444-44444444_0", noGetArguments.GetMap());
139 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Instance, instanceOrthancId));
140 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
141 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
142 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
143
144 accesses.clear();
145 parser.Parse(accesses, "/osimis-viewer/images/44444444-44444444-44444444-44444444-44444444/0/high-quality", noGetArguments.GetMap());
146 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Instance, instanceOrthancId));
147 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
148 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
149 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
150
151 accesses.clear();
152 parser.Parse(accesses, "/system", noGetArguments.GetMap());
153 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_System, "/system"));
154
155
156 ///////////////////////// dicom-web
157 accesses.clear();
158 parser.Parse(accesses, "/dicom-web/studies/2.2", noGetArguments.GetMap());
159 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
160 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
161
162 accesses.clear();
163 parser.Parse(accesses, "/dicom-web/studies/2.2/series/3.3", noGetArguments.GetMap());
164 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
165 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
166 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
167
168 accesses.clear();
169 parser.Parse(accesses, "/dicom-web/studies/2.2/series/3.3/instances/4.4", noGetArguments.GetMap());
170 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Instance, instanceOrthancId));
171 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
172 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
173 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
174
175 accesses.clear();
176 parser.Parse(accesses, "/dicom-web/studies/2.2/series/3.3/instances/4.4/frames/0", noGetArguments.GetMap());
177 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Instance, instanceOrthancId));
178 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
179 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
180 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
181
182 {
183 accesses.clear();
184 const char* getKeys[] = {"0020000D"};
185 const char* getValues[] = {"2.2"};
186 AssociativeArray getArguments(1, getKeys, getValues, false);
187 parser.Parse(accesses, "/dicom-web/studies", getArguments.GetMap());
188 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
189 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
190 }
191 {
192 accesses.clear();
193 const char* getKeys[] = {"0020000D", "0020000E"};
194 const char* getValues[] = {"2.2", "3.3"};
195 AssociativeArray getArguments(2, getKeys, getValues, false);
196 parser.Parse(accesses, "/dicom-web/series", getArguments.GetMap());
197 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
198 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
199 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
200 }
201 {
202 accesses.clear();
203 const char* getKeys[] = {"0020000D", "00080018", "0020000E"};
204 const char* getValues[] = {"2.2", "4.4", "3.3", };
205 AssociativeArray getArguments(3, getKeys, getValues, false);
206 parser.Parse(accesses, "/dicom-web/studies", getArguments.GetMap());
207 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Instance, instanceOrthancId));
208 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId));
209 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId));
210 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
211 }
212 {
213 accesses.clear();
214 const char* getKeys[] = {"00100010"};
215 const char* getValues[] = {"PATIENT.1"};
216 AssociativeArray getArguments(1, getKeys, getValues, false);
217 parser.Parse(accesses, "/dicom-web/studies", getArguments.GetMap());
218 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId));
219 }
220
221 { // qido with no arguments = search all => system resource
222 accesses.clear();
223 parser.Parse(accesses, "/dicom-web/studies", noGetArguments.GetMap());
224 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_System, "/dicom-web/studies"));
225 }
226
227 accesses.clear();
228 parser.Parse(accesses, "/dicom-web/servers", noGetArguments.GetMap());
229 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_System, "/dicom-web/servers"));
230
231 accesses.clear();
232 parser.Parse(accesses, "/dicom-web/info", noGetArguments.GetMap());
233 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_System, "/dicom-web/info"));
234
235 accesses.clear();
236 parser.Parse(accesses, "/dicom-web/servers/test/qido", noGetArguments.GetMap());
237 ASSERT_TRUE(IsAccessing(accesses, AccessLevel_System, "/dicom-web/servers/test/qido"));
238
239 }
240 }
241
242 int main(int argc, char **argv)
243 {
244 ::testing::InitGoogleTest(&argc, argv);
245 return RUN_ALL_TESTS();
246 }