Mercurial > hg > orthanc-authorization
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); |