Mercurial > hg > orthanc-authorization
annotate Plugin/Plugin.cpp @ 32:79d871605ffd
sync
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 30 Jun 2020 07:38:31 +0200 |
parents | c304ffca5d80 |
children | b9c536bf598b 53dbed29949a |
rev | line source |
---|---|
1 | 1 /** |
2 * Advanced authorization plugin for Orthanc | |
31
c304ffca5d80
upgrade to year 2020
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
29
diff
changeset
|
3 * Copyright (C) 2017-2020 Osimis S.A., Belgium |
1 | 4 * |
5 * This program is free software: you can redistribute it and/or | |
6 * modify it under the terms of the GNU Affero General Public License | |
7 * as published by the Free Software Foundation, either version 3 of | |
8 * the License, or (at your option) any later version. | |
9 * | |
10 * This program is distributed in the hope that it will be useful, but | |
11 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Affero General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Affero General Public License | |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 **/ | |
18 | |
19 #include "AssociativeArray.h" | |
20 #include "DefaultAuthorizationParser.h" | |
21 #include "CachedAuthorizationService.h" | |
22 #include "AuthorizationWebService.h" | |
23 #include "MemoryCache.h" | |
24 | |
32 | 25 #include <OrthancPluginCppWrapper.h> |
26 #include <Logging.h> | |
27 #include <Toolbox.h> | |
1 | 28 |
29 | |
30 // Configuration of the authorization plugin | |
31 static std::auto_ptr<OrthancPlugins::IAuthorizationParser> authorizationParser_; | |
32 static std::auto_ptr<OrthancPlugins::IAuthorizationService> authorizationService_; | |
33 static std::set<std::string> uncheckedResources_; | |
34 static std::list<std::string> uncheckedFolders_; | |
35 static std::list<OrthancPlugins::Token> tokens_; | |
36 static std::set<OrthancPlugins::AccessLevel> uncheckedLevels_; | |
37 | |
38 | |
39 static int32_t FilterHttpRequests(OrthancPluginHttpMethod method, | |
40 const char *uri, | |
41 const char *ip, | |
42 uint32_t headersCount, | |
43 const char *const *headersKeys, | |
44 const char *const *headersValues, | |
45 uint32_t getArgumentsCount, | |
46 const char *const *getArgumentsKeys, | |
47 const char *const *getArgumentsValues) | |
48 { | |
49 try | |
50 { | |
51 if (method == OrthancPluginHttpMethod_Get) | |
52 { | |
53 // Allow GET accesses to static resources | |
54 if (uncheckedResources_.find(uri) != uncheckedResources_.end()) | |
55 { | |
56 return 1; | |
57 } | |
58 | |
59 for (std::list<std::string>::const_iterator | |
60 it = uncheckedFolders_.begin(); it != uncheckedFolders_.end(); ++it) | |
61 { | |
62 if (Orthanc::Toolbox::StartsWith(uri, *it)) | |
63 { | |
64 return 1; | |
65 } | |
66 } | |
67 } | |
68 | |
69 if (authorizationParser_.get() != NULL && | |
70 authorizationService_.get() != NULL) | |
71 { | |
72 // Parse the resources that are accessed through this URI | |
73 OrthancPlugins::IAuthorizationParser::AccessedResources accesses; | |
74 if (!authorizationParser_->Parse(accesses, uri)) | |
75 { | |
76 return 0; // Unable to parse this URI | |
77 } | |
78 | |
79 // Loop over all the accessed resources to ensure access is | |
80 // granted to each of them | |
81 for (OrthancPlugins::IAuthorizationParser::AccessedResources::const_iterator | |
82 access = accesses.begin(); access != accesses.end(); ++access) | |
83 { | |
84 // Ignored the access levels that are unchecked | |
85 // (cf. "UncheckedLevels" option) | |
86 if (uncheckedLevels_.find(access->GetLevel()) == uncheckedLevels_.end()) | |
87 { | |
88 LOG(INFO) << "Testing whether access to " | |
89 << OrthancPlugins::EnumerationToString(access->GetLevel()) | |
90 << " \"" << access->GetOrthancId() << "\" is allowed"; | |
91 | |
92 bool granted = false; | |
93 unsigned int validity; // ignored | |
94 | |
95 if (tokens_.empty()) | |
96 { | |
97 granted = authorizationService_->IsGranted(validity, method, *access); | |
98 } | |
99 else | |
100 { | |
101 OrthancPlugins::AssociativeArray headers | |
102 (headersCount, headersKeys, headersValues, false); | |
103 | |
104 OrthancPlugins::AssociativeArray getArguments | |
105 (getArgumentsCount, getArgumentsKeys, getArgumentsValues, true); | |
106 | |
107 // Loop over all the authorization tokens stored in the HTTP | |
108 // headers, until finding one that is granted | |
109 for (std::list<OrthancPlugins::Token>::const_iterator | |
110 token = tokens_.begin(); token != tokens_.end(); ++token) | |
111 { | |
112 std::string value; | |
113 | |
114 bool hasValue = false; | |
115 switch (token->GetType()) | |
116 { | |
117 case OrthancPlugins::TokenType_HttpHeader: | |
118 hasValue = headers.GetValue(value, token->GetKey()); | |
119 break; | |
120 | |
121 case OrthancPlugins::TokenType_GetArgument: | |
122 hasValue = getArguments.GetValue(value, token->GetKey()); | |
123 break; | |
124 | |
125 default: | |
126 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
127 } | |
128 | |
129 if (hasValue && | |
130 authorizationService_->IsGranted(validity, method, *access, *token, value)) | |
131 { | |
132 granted = true; | |
133 break; | |
134 } | |
135 } | |
136 } | |
137 | |
138 if (!granted) | |
139 { | |
140 return 0; | |
141 } | |
142 } | |
143 } | |
144 | |
145 // Access is granted to all the resources | |
146 return 1; | |
147 } | |
148 | |
149 // By default, forbid access to all the resources | |
150 return 0; | |
151 } | |
152 catch (std::runtime_error& e) | |
153 { | |
154 LOG(ERROR) << e.what(); | |
155 return OrthancPluginErrorCode_Success; // Ignore error | |
156 } | |
157 catch (Orthanc::OrthancException& e) | |
158 { | |
159 LOG(ERROR) << e.What(); | |
160 return OrthancPluginErrorCode_Success; // Ignore error | |
161 } | |
162 catch (...) | |
163 { | |
164 LOG(ERROR) << "Unhandled internal exception"; | |
165 return OrthancPluginErrorCode_Success; // Ignore error | |
166 } | |
167 } | |
168 | |
169 | |
170 #if !ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 1) | |
171 static int32_t FilterHttpRequestsFallback(OrthancPluginHttpMethod method, | |
172 const char *uri, | |
173 const char *ip, | |
174 uint32_t headersCount, | |
175 const char *const *headersKeys, | |
176 const char *const *headersValues) | |
177 { | |
178 // Fallback wrapper function for Orthanc <= 1.2.0, where the GET | |
179 // arguments were not available in the HTTP filters | |
180 return FilterHttpRequests(method, uri, ip, | |
181 headersCount, headersKeys, headersValues, | |
182 0, NULL, NULL); | |
183 } | |
184 #endif | |
185 | |
186 | |
187 static OrthancPluginErrorCode OnChangeCallback(OrthancPluginChangeType changeType, | |
188 OrthancPluginResourceType resourceType, | |
189 const char* resourceId) | |
190 { | |
191 try | |
192 { | |
193 if (authorizationParser_.get() == NULL) | |
194 { | |
195 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
196 } | |
197 | |
198 if (changeType == OrthancPluginChangeType_Deleted) | |
199 { | |
200 switch (resourceType) | |
201 { | |
202 case OrthancPluginResourceType_Patient: | |
203 authorizationParser_->Invalidate(Orthanc::ResourceType_Patient, resourceId); | |
204 break; | |
205 | |
206 case OrthancPluginResourceType_Study: | |
207 authorizationParser_->Invalidate(Orthanc::ResourceType_Study, resourceId); | |
208 break; | |
209 | |
210 case OrthancPluginResourceType_Series: | |
211 authorizationParser_->Invalidate(Orthanc::ResourceType_Series, resourceId); | |
212 break; | |
213 | |
214 case OrthancPluginResourceType_Instance: | |
215 authorizationParser_->Invalidate(Orthanc::ResourceType_Instance, resourceId); | |
216 break; | |
217 | |
218 default: | |
219 break; | |
220 } | |
221 } | |
222 | |
223 return OrthancPluginErrorCode_Success; | |
224 } | |
225 catch (std::runtime_error& e) | |
226 { | |
227 LOG(ERROR) << e.what(); | |
228 return OrthancPluginErrorCode_Success; // Ignore error | |
229 } | |
230 catch (Orthanc::OrthancException& e) | |
231 { | |
232 LOG(ERROR) << e.What(); | |
233 return OrthancPluginErrorCode_Success; // Ignore error | |
234 } | |
235 catch (...) | |
236 { | |
237 LOG(ERROR) << "Unhandled internal exception"; | |
238 return OrthancPluginErrorCode_Success; // Ignore error | |
239 } | |
240 } | |
241 | |
242 | |
243 extern "C" | |
244 { | |
245 ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context) | |
246 { | |
29
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
247 OrthancPlugins::SetGlobalContext(context); |
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
248 OrthancPluginLogWarning(context, "Initializing the authorization plugin"); |
1 | 249 |
250 /* Check the version of the Orthanc core */ | |
29
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
251 if (OrthancPluginCheckVersion(context) == 0) |
1 | 252 { |
29
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
253 OrthancPlugins::ReportMinimalOrthancVersion(ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER, |
1 | 254 ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER, |
255 ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER); | |
256 return -1; | |
257 } | |
258 | |
32 | 259 Orthanc::Logging::InitializePluginContext(context); |
29
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
260 OrthancPluginSetDescription(context, "Advanced authorization plugin for Orthanc."); |
1 | 261 |
262 try | |
263 { | |
29
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
264 OrthancPlugins::OrthancConfiguration general; |
1 | 265 |
266 static const char* SECTION = "Authorization"; | |
267 if (general.IsSection(SECTION)) | |
268 { | |
269 OrthancPlugins::OrthancConfiguration configuration; | |
270 general.GetSection(configuration, "Authorization"); | |
271 | |
272 // TODO - The size of the caches is set to 10,000 items. Maybe add a configuration option? | |
273 OrthancPlugins::MemoryCache::Factory factory(10000); | |
274 | |
275 { | |
276 std::string root; | |
277 | |
278 if (configuration.IsSection("DicomWeb")) | |
279 { | |
280 OrthancPlugins::OrthancConfiguration dicomWeb; | |
281 dicomWeb.GetSection(configuration, "DicomWeb"); | |
282 root = dicomWeb.GetStringValue("Root", ""); | |
283 } | |
284 | |
285 if (root.empty()) | |
286 { | |
287 root = "/dicom-web/"; | |
288 } | |
289 | |
290 authorizationParser_.reset | |
29
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
291 (new OrthancPlugins::DefaultAuthorizationParser(factory, root)); |
1 | 292 } |
293 | |
294 std::list<std::string> tmp; | |
295 | |
296 configuration.LookupListOfStrings(tmp, "TokenHttpHeaders", true); | |
297 for (std::list<std::string>::const_iterator | |
298 it = tmp.begin(); it != tmp.end(); ++it) | |
299 { | |
300 tokens_.push_back(OrthancPlugins::Token(OrthancPlugins::TokenType_HttpHeader, *it)); | |
301 } | |
302 | |
303 configuration.LookupListOfStrings(tmp, "TokenGetArguments", true); | |
304 | |
8
4362026afddf
orthanc 1.2.1 renamed as 1.3.0
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1
diff
changeset
|
305 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 3, 0) |
1 | 306 for (std::list<std::string>::const_iterator |
307 it = tmp.begin(); it != tmp.end(); ++it) | |
308 { | |
309 tokens_.push_back(OrthancPlugins::Token(OrthancPlugins::TokenType_GetArgument, *it)); | |
310 } | |
311 #else | |
312 if (!tmp.empty()) | |
313 { | |
29
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
314 throw Orthanc::OrthancException( |
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
315 Orthanc::ErrorCode_Plugin, |
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
316 "The option \"TokenGetArguments\" of the authorization plugin " |
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
317 "is only valid if compiled against Orthanc >= 1.3.0" |
1 | 318 } |
319 #endif | |
320 | |
321 configuration.LookupSetOfStrings(uncheckedResources_, "UncheckedResources", false); | |
322 configuration.LookupListOfStrings(uncheckedFolders_, "UncheckedFolders", false); | |
323 | |
324 std::string url; | |
325 | |
326 static const char* WEB_SERVICE = "WebService"; | |
327 if (!configuration.LookupStringValue(url, WEB_SERVICE)) | |
328 { | |
29
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
329 throw Orthanc::OrthancException( |
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
330 Orthanc::ErrorCode_BadFileFormat, |
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
331 "Missing mandatory option \"" + std::string(WEB_SERVICE) + |
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
332 "\" for the authorization plugin"); |
1 | 333 } |
334 | |
335 if (configuration.LookupListOfStrings(tmp, "UncheckedLevels", false)) | |
336 { | |
337 for (std::list<std::string>::const_iterator | |
338 it = tmp.begin(); it != tmp.end(); ++it) | |
339 { | |
340 uncheckedLevels_.insert(OrthancPlugins::StringToAccessLevel(*it)); | |
341 } | |
342 } | |
343 | |
344 authorizationService_.reset | |
345 (new OrthancPlugins::CachedAuthorizationService | |
29
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
346 (new OrthancPlugins::AuthorizationWebService(url), factory)); |
1 | 347 |
29
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
348 OrthancPluginRegisterOnChangeCallback(context, OnChangeCallback); |
1 | 349 |
350 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 1) | |
29
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
351 OrthancPluginRegisterIncomingHttpRequestFilter2(context, FilterHttpRequests); |
1 | 352 #else |
29
bc0431cb6b8f
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
22
diff
changeset
|
353 OrthancPluginRegisterIncomingHttpRequestFilter(context, FilterHttpRequestsFallback); |
1 | 354 #endif |
355 } | |
356 else | |
357 { | |
358 LOG(WARNING) << "No section \"" << SECTION << "\" in the configuration file, " | |
359 << "the authorization plugin is disabled"; | |
360 } | |
361 } | |
362 catch (Orthanc::OrthancException& e) | |
363 { | |
364 LOG(ERROR) << e.What(); | |
365 return -1; | |
366 } | |
367 | |
368 return 0; | |
369 } | |
370 | |
371 | |
372 ORTHANC_PLUGINS_API void OrthancPluginFinalize() | |
373 { | |
374 authorizationParser_.reset(NULL); | |
375 } | |
376 | |
377 | |
378 ORTHANC_PLUGINS_API const char* OrthancPluginGetName() | |
379 { | |
380 return "authorization"; | |
381 } | |
382 | |
383 | |
384 ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion() | |
385 { | |
22
c44013681a51
now using the Orthanc framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
8
diff
changeset
|
386 return ORTHANC_PLUGIN_VERSION; |
1 | 387 } |
388 } |