comparison Plugin/Plugin.cpp @ 1:d5d3cb00556a

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