comparison Plugin/Plugin.cpp @ 116:89eddd4b2f6a

tested resource token for WADO-RS
author Alain Mazy <am@osimis.io>
date Sat, 09 Sep 2023 13:17:38 +0200
parents 0eed78c1e177
children 968042b7df4c
comparison
equal deleted inserted replaced
115:0eed78c1e177 116:89eddd4b2f6a
146 146
147 // This method only checks if a resource is accessible thanks to its labels. If we could not check it, we always return false !! 147 // This method only checks if a resource is accessible thanks to its labels. If we could not check it, we always return false !!
148 return false; // we could not check labels 148 return false; // we could not check labels
149 } 149 }
150 150
151
152 static void GetAuthTokens(std::vector<TokenAndValue>& authTokens,
153 uint32_t headersCount,
154 const char *const *headersKeys,
155 const char *const *headersValues,
156 uint32_t getArgumentsCount,
157 const char *const *getArgumentsKeys,
158 const char *const *getArgumentsValues) // the tokens that are set in this request
159 {
160 // Extract auth tokens from headers and url get arguments
161 ////////////////////////////////////////////////////////////////
162
163 OrthancPlugins::AssociativeArray headers(headersCount, headersKeys, headersValues, false);
164 OrthancPlugins::AssociativeArray getArguments(getArgumentsCount, getArgumentsKeys, getArgumentsValues, true);
165
166 for (std::set<OrthancPlugins::Token>::const_iterator token = tokens_.begin(); token != tokens_.end(); ++token)
167 {
168 std::string value;
169
170 bool hasValue = false;
171 switch (token->GetType())
172 {
173 case OrthancPlugins::TokenType_HttpHeader:
174 hasValue = headers.GetValue(value, token->GetKey());
175 break;
176
177 case OrthancPlugins::TokenType_GetArgument:
178 hasValue = getArguments.GetValue(value, token->GetKey());
179 break;
180
181 default:
182 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
183 }
184
185 if (hasValue)
186 {
187 authTokens.push_back(TokenAndValue(*token, value));
188 }
189 }
190 }
191
192 static bool IsResourceAccessGranted(const std::vector<TokenAndValue>& authTokens,
193 OrthancPluginHttpMethod method,
194 const OrthancPlugins::AccessedResource& access)
195 {
196 unsigned int validity; // ignored
197
198 // Ignored the access levels that are unchecked
199 // (cf. "UncheckedLevels" option)
200 if (uncheckedLevels_.find(access.GetLevel()) == uncheckedLevels_.end())
201 {
202 std::string msg = std::string("Testing whether access to ") + OrthancPlugins::EnumerationToString(access.GetLevel()) + " \"" + access.GetOrthancId() + "\" is allowed with a resource token";
203 LOG(INFO) << msg;
204
205 bool granted = false;
206
207 if (authTokens.empty())
208 {
209 granted = authorizationService_->IsGrantedToAnonymousUser(validity, method, access);
210 }
211 else
212 {
213 // Loop over all the authorization tokens in the request until finding one that is granted
214 for (size_t i = 0; i < authTokens.size(); ++i)
215 {
216 if (authorizationService_->IsGranted(validity, method, access, authTokens[i].GetToken(), authTokens[i].GetValue()))
217 {
218 granted = true;
219 break;
220 }
221 }
222 }
223
224 if (!granted)
225 {
226 LOG(INFO) << msg << " -> not granted";
227 return false;
228 }
229 else
230 {
231 LOG(INFO) << msg << " -> granted";
232 return true;
233 }
234 }
235
236 return false;
237 }
238
151 static int32_t FilterHttpRequests(OrthancPluginHttpMethod method, 239 static int32_t FilterHttpRequests(OrthancPluginHttpMethod method,
152 const char *uri, 240 const char *uri,
153 const char *ip, 241 const char *ip,
154 uint32_t headersCount, 242 uint32_t headersCount,
155 const char *const *headersKeys, 243 const char *const *headersKeys,
180 return 1; 268 return 1;
181 } 269 }
182 } 270 }
183 } 271 }
184 272
185 // Extract auth tokens from headers and url get arguments 273 std::vector<TokenAndValue> authTokens; // the tokens that are set in this request
186 //////////////////////////////////////////////////////////////// 274 GetAuthTokens(authTokens, headersCount, headersKeys, headersValues, getArgumentsCount, getArgumentsKeys, getArgumentsValues);
187 275
188 OrthancPlugins::AssociativeArray headers(headersCount, headersKeys, headersValues, false);
189 OrthancPlugins::AssociativeArray getArguments(getArgumentsCount, getArgumentsKeys, getArgumentsValues, true); 276 OrthancPlugins::AssociativeArray getArguments(getArgumentsCount, getArgumentsKeys, getArgumentsValues, true);
190
191 std::vector<TokenAndValue> authTokens; // the tokens that are set in this request
192
193 for (std::set<OrthancPlugins::Token>::const_iterator token = tokens_.begin(); token != tokens_.end(); ++token)
194 {
195 std::string value;
196
197 bool hasValue = false;
198 switch (token->GetType())
199 {
200 case OrthancPlugins::TokenType_HttpHeader:
201 hasValue = headers.GetValue(value, token->GetKey());
202 break;
203
204 case OrthancPlugins::TokenType_GetArgument:
205 hasValue = getArguments.GetValue(value, token->GetKey());
206 break;
207
208 default:
209 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
210 }
211
212 if (hasValue)
213 {
214 authTokens.push_back(TokenAndValue(*token, value));
215 }
216 }
217 277
218 // Based on the tokens, check if the user has access based on its permissions and the mapping between urls and permissions 278 // Based on the tokens, check if the user has access based on its permissions and the mapping between urls and permissions
219 //////////////////////////////////////////////////////////////// 279 ////////////////////////////////////////////////////////////////
220 bool hasUserRequiredPermissions = false; 280 bool hasUserRequiredPermissions = false;
221 bool hasAuthorizedLabelsForResource = false; 281 bool hasAuthorizedLabelsForResource = false;
308 // Loop over all the accessed resources to ensure access is 368 // Loop over all the accessed resources to ensure access is
309 // granted to each of them 369 // granted to each of them
310 for (OrthancPlugins::IAuthorizationParser::AccessedResources::const_iterator 370 for (OrthancPlugins::IAuthorizationParser::AccessedResources::const_iterator
311 access = accesses.begin(); access != accesses.end(); ++access) 371 access = accesses.begin(); access != accesses.end(); ++access)
312 { 372 {
313 // Ignored the access levels that are unchecked 373 if (IsResourceAccessGranted(authTokens, method, *access))
314 // (cf. "UncheckedLevels" option) 374 {
315 if (uncheckedLevels_.find(access->GetLevel()) == uncheckedLevels_.end()) 375 return 1;
316 {
317 std::string msg = std::string("Testing whether access to ") + OrthancPlugins::EnumerationToString(access->GetLevel()) + " \"" + access->GetOrthancId() + "\" is allowed with a resource token";
318 LOG(INFO) << msg;
319
320 bool granted = false;
321
322 if (authTokens.empty())
323 {
324 granted = authorizationService_->IsGrantedToAnonymousUser(validity, method, *access);
325 }
326 else
327 {
328 // Loop over all the authorization tokens in the request until finding one that is granted
329 for (size_t i = 0; i < authTokens.size(); ++i)
330 {
331 if (authorizationService_->IsGranted(validity, method, *access, authTokens[i].GetToken(), authTokens[i].GetValue()))
332 {
333 granted = true;
334 break;
335 }
336 }
337 }
338
339 if (!granted)
340 {
341 LOG(INFO) << msg << " -> not granted";
342 return 0;
343 }
344 else
345 {
346 LOG(INFO) << msg << " -> granted";
347 return 1;
348 }
349 } 376 }
350 } 377 }
351 } 378 }
352 379
353 // By default, forbid access to all the resources 380 // By default, forbid access to all the resources
583 610
584 // If the logged in user has restrictions on the labels he can access, modify the tools/find payload before reposting it to Orthanc 611 // If the logged in user has restrictions on the labels he can access, modify the tools/find payload before reposting it to Orthanc
585 OrthancPlugins::IAuthorizationService::UserProfile profile; 612 OrthancPlugins::IAuthorizationService::UserProfile profile;
586 if (GetUserProfileInternal(profile, request)) 613 if (GetUserProfileInternal(profile, request))
587 { 614 {
588 AdjustToolsFindQueryLabels(body, profile); 615 if (!HasAccessToSomeLabels(profile))
616 {
617 // If anonymous user profile, it might be a resource token e.g accessing /dicom-web/studies/.../metadata
618 // -> extract the StudyInstanceUID from the query and send the token for validation to the auth-service
619 // If there is no StudyInstanceUID, then, return a 403 because we don't know what resource it relates to
620 if (!body.isMember("Query") || !body["Query"].isMember("StudyInstanceUID"))
621 {
622 throw Orthanc::OrthancException(Orthanc::ErrorCode_ForbiddenAccess, "Auth plugin: unable to call tools/find when the user does not have access to any labels and if there is no StudyInstanceUID in the query.");
623 }
624
625 std::vector<TokenAndValue> authTokens; // the tokens that are set in this request
626 GetAuthTokens(authTokens, request->headersCount, request->headersKeys, request->headersValues, request->getCount, request->getKeys, request->getValues);
627
628
629 std::string studyInstanceUID = body["Query"]["StudyInstanceUID"].asString();
630 Json::Value studyOrhtancIds;
631 if (!OrthancPlugins::RestApiPost(studyOrhtancIds, "/tools/lookup", studyInstanceUID, false) || studyOrhtancIds.size() != 1)
632 {
633 throw Orthanc::OrthancException(Orthanc::ErrorCode_ForbiddenAccess, "Auth plugin: when using tools/find with a resource token, unable to get the orthanc ID of StudyInstanceUID specified in the query.");
634 }
635
636 std::set<std::string> labels;
637 OrthancPlugins::AccessedResource accessedResource(Orthanc::ResourceType_Study, studyOrhtancIds[0]["ID"].asString(), studyInstanceUID, labels);
638 if (!IsResourceAccessGranted(authTokens, request->method, accessedResource))
639 {
640 throw Orthanc::OrthancException(Orthanc::ErrorCode_ForbiddenAccess, "Auth plugin: when using tools/find with a resource token, the resource must grant access to the StudyInstanceUID specified in the query.");
641 }
642
643 }
644 else
645 {
646 AdjustToolsFindQueryLabels(body, profile);
647 }
589 648
590 Json::Value result; 649 Json::Value result;
591 if (OrthancPlugins::RestApiPost(result, "/tools/find", body, false)) 650 if (OrthancPlugins::RestApiPost(result, "/tools/find", body, false))
592 { 651 {
593 OrthancPlugins::AnswerJson(result, output); 652 OrthancPlugins::AnswerJson(result, output);