Mercurial > hg > orthanc-authorization
comparison Plugin/Plugin.cpp @ 124:d5232d374fd8 0.6.0
Add support for ForbiddenAccess error for Orthanc < 1.12.2
author | Alain Mazy <am@osimis.io> |
---|---|
date | Mon, 18 Sep 2023 21:15:33 +0200 |
parents | 9ebcda90587f |
children | 8b123c2adb69 |
comparison
equal
deleted
inserted
replaced
122:db60c361e395 | 124:d5232d374fd8 |
---|---|
46 std::string out; | 46 std::string out; |
47 std::set<std::string> copy = values; // TODO: remove after upgrading to OrthancFramework 1.11.3+ | 47 std::set<std::string> copy = values; // TODO: remove after upgrading to OrthancFramework 1.11.3+ |
48 Orthanc::Toolbox::JoinStrings(out, copy, "|"); | 48 Orthanc::Toolbox::JoinStrings(out, copy, "|"); |
49 return out; | 49 return out; |
50 } | 50 } |
51 | |
52 | |
53 // For Orthanc prior to 1.12.2, we can not use the Forbidden error code and report the error ourselves | |
54 static void SendForbiddenError(const char* message, OrthancPluginRestOutput* output) | |
55 { | |
56 OrthancPluginContext* context = OrthancPlugins::GetGlobalContext(); | |
57 | |
58 OrthancPluginSendHttpStatus(context, output, 403, message, strlen(message)); | |
59 } | |
60 | |
61 | |
51 | 62 |
52 class TokenAndValue | 63 class TokenAndValue |
53 { | 64 { |
54 private: | 65 private: |
55 OrthancPlugins::Token token_; | 66 OrthancPlugins::Token token_; |
624 const char* /*url*/, | 635 const char* /*url*/, |
625 const OrthancPluginHttpRequest* request) | 636 const OrthancPluginHttpRequest* request) |
626 { | 637 { |
627 OrthancPluginContext* context = OrthancPlugins::GetGlobalContext(); | 638 OrthancPluginContext* context = OrthancPlugins::GetGlobalContext(); |
628 | 639 |
629 if (request->method != OrthancPluginHttpMethod_Post) | 640 try |
630 { | 641 { |
631 OrthancPluginSendMethodNotAllowed(context, output, "POST"); | 642 if (request->method != OrthancPluginHttpMethod_Post) |
632 } | 643 { |
633 else | 644 OrthancPluginSendMethodNotAllowed(context, output, "POST"); |
634 { | 645 } |
635 // The filtering to this route is performed by this plugin as it is done for any other route before we get here. | 646 else |
636 | 647 { |
637 Json::Value body; | 648 // The filtering to this route is performed by this plugin as it is done for any other route before we get here. |
638 if (!OrthancPlugins::ReadJson(body, request->body, request->bodySize)) | 649 |
639 { | 650 Json::Value body; |
640 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "A JSON payload was expected"); | 651 if (!OrthancPlugins::ReadJson(body, request->body, request->bodySize)) |
641 } | 652 { |
642 | 653 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "A JSON payload was expected"); |
643 // If the logged in user has restrictions on the labels he can access, modify the tools/find payload before reposting it to Orthanc | 654 } |
644 OrthancPlugins::IAuthorizationService::UserProfile profile; | 655 |
645 if (GetUserProfileInternal(profile, request) && HasAccessToSomeLabels(profile)) | 656 // If the logged in user has restrictions on the labels he can access, modify the tools/find payload before reposting it to Orthanc |
646 { | 657 OrthancPlugins::IAuthorizationService::UserProfile profile; |
647 AdjustToolsFindQueryLabels(body, profile); | 658 if (GetUserProfileInternal(profile, request) && HasAccessToSomeLabels(profile)) |
648 } | 659 { |
649 else // anonymous user profile or resource token | 660 AdjustToolsFindQueryLabels(body, profile); |
650 { | 661 } |
651 std::string studyInstanceUID; | 662 else // anonymous user profile or resource token |
652 | 663 { |
653 // If anonymous user profile, it might be a resource token e.g accessing /dicom-web/studies/.../metadata | 664 std::string studyInstanceUID; |
654 // -> extract the StudyInstanceUID from the query and send the token for validation to the auth-service | 665 |
655 // If there is no StudyInstanceUID, then, return a 403 because we don't know what resource it relates to | 666 // If anonymous user profile, it might be a resource token e.g accessing /dicom-web/studies/.../metadata |
656 if (!GetStudyInstanceUIDFromQuery(studyInstanceUID, body)) | 667 // -> extract the StudyInstanceUID from the query and send the token for validation to the auth-service |
657 { | 668 // If there is no StudyInstanceUID, then, return a 403 because we don't know what resource it relates to |
658 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."); | 669 if (!GetStudyInstanceUIDFromQuery(studyInstanceUID, body)) |
659 } | 670 { |
660 | 671 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."); |
661 Json::Value studyOrhtancIds; | 672 } |
662 if (!OrthancPlugins::RestApiPost(studyOrhtancIds, "/tools/lookup", studyInstanceUID, false) || studyOrhtancIds.size() != 1) | 673 |
663 { | 674 Json::Value studyOrhtancIds; |
664 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."); | 675 if (!OrthancPlugins::RestApiPost(studyOrhtancIds, "/tools/lookup", studyInstanceUID, false) || studyOrhtancIds.size() != 1) |
665 } | 676 { |
666 | 677 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."); |
667 std::vector<TokenAndValue> authTokens; // the tokens that are set in this request | 678 } |
668 GetAuthTokens(authTokens, request->headersCount, request->headersKeys, request->headersValues, request->getCount, request->getKeys, request->getValues); | 679 |
669 | 680 std::vector<TokenAndValue> authTokens; // the tokens that are set in this request |
670 std::set<std::string> labels; | 681 GetAuthTokens(authTokens, request->headersCount, request->headersKeys, request->headersValues, request->getCount, request->getKeys, request->getValues); |
671 OrthancPlugins::AccessedResource accessedResource(Orthanc::ResourceType_Study, studyOrhtancIds[0]["ID"].asString(), studyInstanceUID, labels); | 682 |
672 if (!IsResourceAccessGranted(authTokens, request->method, accessedResource)) | 683 std::set<std::string> labels; |
673 { | 684 OrthancPlugins::AccessedResource accessedResource(Orthanc::ResourceType_Study, studyOrhtancIds[0]["ID"].asString(), studyInstanceUID, labels); |
674 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."); | 685 if (!IsResourceAccessGranted(authTokens, request->method, accessedResource)) |
675 } | 686 { |
676 | 687 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."); |
677 } | 688 } |
678 | 689 |
679 Json::Value result; | 690 } |
680 if (OrthancPlugins::RestApiPost(result, "/tools/find", body, false)) | 691 |
681 { | 692 Json::Value result; |
682 OrthancPlugins::AnswerJson(result, output); | 693 if (OrthancPlugins::RestApiPost(result, "/tools/find", body, false)) |
683 } | 694 { |
684 | 695 OrthancPlugins::AnswerJson(result, output); |
685 } | 696 } |
697 | |
698 } | |
699 | |
700 } | |
701 catch(const Orthanc::OrthancException& e) | |
702 { | |
703 // this error is not yet supported in Orthanc 1.12.1 | |
704 if (e.GetErrorCode() == Orthanc::ErrorCode_ForbiddenAccess && !OrthancPlugins::CheckMinimalOrthancVersion(1, 12, 2)) | |
705 { | |
706 SendForbiddenError(e.GetDetails(), output); | |
707 } | |
708 else | |
709 { | |
710 throw e; | |
711 } | |
712 } | |
713 | |
686 } | 714 } |
687 | 715 |
688 void ToolsLabels(OrthancPluginRestOutput* output, | 716 void ToolsLabels(OrthancPluginRestOutput* output, |
689 const char* /*url*/, | 717 const char* /*url*/, |
690 const OrthancPluginHttpRequest* request) | 718 const OrthancPluginHttpRequest* request) |
691 { | 719 { |
692 OrthancPluginContext* context = OrthancPlugins::GetGlobalContext(); | 720 OrthancPluginContext* context = OrthancPlugins::GetGlobalContext(); |
693 | 721 |
694 if (request->method != OrthancPluginHttpMethod_Get) | 722 try |
695 { | 723 { |
696 OrthancPluginSendMethodNotAllowed(context, output, "GET"); | 724 if (request->method != OrthancPluginHttpMethod_Get) |
697 } | 725 { |
698 else | 726 OrthancPluginSendMethodNotAllowed(context, output, "GET"); |
699 { | |
700 // The filtering to this route is performed by this plugin as it is done for any other route before we get here. | |
701 | |
702 // If the logged in user has restrictions on the labels he can access, modify the tools/labels response before answering | |
703 OrthancPlugins::IAuthorizationService::UserProfile profile; | |
704 if (GetUserProfileInternal(profile, request)) | |
705 { | |
706 if (!HasAccessToSomeLabels(profile)) | |
707 { | |
708 Json::Value emptyLabels; | |
709 OrthancPlugins::AnswerJson(emptyLabels, output); | |
710 return; | |
711 } | |
712 | |
713 Json::Value jsonLabels; | |
714 if (OrthancPlugins::RestApiGet(jsonLabels, "/tools/labels", false)) | |
715 { | |
716 std::set<std::string> allLabels; | |
717 Orthanc::SerializationToolbox::ReadSetOfStrings(allLabels, jsonLabels); | |
718 | |
719 if (!HasAccessToAllLabels(profile)) | |
720 { | |
721 std::set<std::string> authorizedLabels; | |
722 | |
723 Orthanc::Toolbox::GetIntersection(authorizedLabels, allLabels, profile.authorizedLabels); | |
724 Orthanc::SerializationToolbox::WriteSetOfStrings(jsonLabels, authorizedLabels); | |
725 } | |
726 OrthancPlugins::AnswerJson(jsonLabels, output); | |
727 } | |
728 | |
729 } | 727 } |
730 else | 728 else |
731 { | 729 { |
732 throw Orthanc::OrthancException(Orthanc::ErrorCode_ForbiddenAccess, "Auth plugin: no user profile found, access to tools/labels is forbidden."); | 730 // The filtering to this route is performed by this plugin as it is done for any other route before we get here. |
731 | |
732 // If the logged in user has restrictions on the labels he can access, modify the tools/labels response before answering | |
733 OrthancPlugins::IAuthorizationService::UserProfile profile; | |
734 if (GetUserProfileInternal(profile, request)) | |
735 { | |
736 if (!HasAccessToSomeLabels(profile)) | |
737 { | |
738 Json::Value emptyLabels; | |
739 OrthancPlugins::AnswerJson(emptyLabels, output); | |
740 return; | |
741 } | |
742 | |
743 Json::Value jsonLabels; | |
744 if (OrthancPlugins::RestApiGet(jsonLabels, "/tools/labels", false)) | |
745 { | |
746 std::set<std::string> allLabels; | |
747 Orthanc::SerializationToolbox::ReadSetOfStrings(allLabels, jsonLabels); | |
748 | |
749 if (!HasAccessToAllLabels(profile)) | |
750 { | |
751 std::set<std::string> authorizedLabels; | |
752 | |
753 Orthanc::Toolbox::GetIntersection(authorizedLabels, allLabels, profile.authorizedLabels); | |
754 Orthanc::SerializationToolbox::WriteSetOfStrings(jsonLabels, authorizedLabels); | |
755 } | |
756 OrthancPlugins::AnswerJson(jsonLabels, output); | |
757 } | |
758 | |
759 } | |
760 else | |
761 { | |
762 throw Orthanc::OrthancException(Orthanc::ErrorCode_ForbiddenAccess, "Auth plugin: no user profile found, access to tools/labels is forbidden."); | |
763 } | |
764 } | |
765 } | |
766 catch(const Orthanc::OrthancException& e) | |
767 { | |
768 // this error is not yet supported in Orthanc 1.12.1 | |
769 if (e.GetErrorCode() == Orthanc::ErrorCode_ForbiddenAccess && !OrthancPlugins::CheckMinimalOrthancVersion(1, 12, 2)) | |
770 { | |
771 SendForbiddenError(e.GetDetails(), output); | |
772 } | |
773 else | |
774 { | |
775 throw e; | |
733 } | 776 } |
734 } | 777 } |
735 } | 778 } |
736 | 779 |
737 | 780 |