Mercurial > hg > orthanc-authorization
diff UnitTestsSources/UnitTestsMain.cpp @ 77:94a9484d7f8f
fix security issues allowing to browse remote dicom servers + introduced UnitTests
author | Alain Mazy <am@osimis.io> |
---|---|
date | Wed, 15 Mar 2023 16:36:42 +0100 |
parents | |
children | 0ffad746a16b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/UnitTestsMain.cpp Wed Mar 15 16:36:42 2023 +0100 @@ -0,0 +1,246 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2021 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include <gtest/gtest.h> +#include <boost/lexical_cast.hpp> +#include <boost/algorithm/string/predicate.hpp> + +#include "../Plugin/DefaultAuthorizationParser.h" +#include "../Plugin/AssociativeArray.h" +#include "../Plugin/AccessedResource.h" +#include "../Plugin/MemoryCache.h" +#include "../Plugin/PermissionParser.h" +#include "../Plugin/ResourceHierarchyCache.h" + +using namespace OrthancPlugins; + +std::string instanceOrthancId = "44444444-44444444-44444444-44444444-44444444"; +std::string seriesOrthancId = "33333333-33333333-33333333-33333333-33333333"; +std::string studyOrthancId = "22222222-22222222-22222222-22222222-22222222"; +std::string patientOrthancId = "11111111-11111111-11111111-11111111-11111111"; + +std::string instanceDicomUid = "4.4"; +std::string seriesDicomUid = "3.3"; +std::string studyDicomUid = "2.2"; +std::string patientDicomUid = "PATIENT.1"; + +bool IsAccessing(const IAuthorizationParser::AccessedResources& accesses, AccessLevel level, const std::string& orthancId) +{ + for (IAuthorizationParser::AccessedResources::const_iterator it = accesses.begin(); it != accesses.end(); ++it) + { + if (it->GetLevel() == level && it->GetOrthancId() == orthancId) + { + return true; + } + } + return false; +} + +namespace OrthancPlugins +{ + // The namespace is necessary for friend classes to work + // http://code.google.com/p/googletest/wiki/AdvancedGuide#Private_Class_Members + +TEST(DefaultAuthorizationParser, Parse) +{ + MemoryCache::Factory factory(10); + DefaultAuthorizationParser parser(factory, "/dicom-web/"); + ResourceHierarchyCache* cache = parser.GetResourceHierarchy(); + + cache->AddOrthancDicomMapping(Orthanc::ResourceType_Instance, instanceOrthancId, instanceDicomUid); + cache->AddOrthancDicomMapping(Orthanc::ResourceType_Series, seriesOrthancId, seriesDicomUid); + cache->AddOrthancDicomMapping(Orthanc::ResourceType_Study, studyOrthancId, studyDicomUid); + cache->AddOrthancDicomMapping(Orthanc::ResourceType_Patient, patientOrthancId, patientDicomUid); + + cache->AddParentLink(Orthanc::ResourceType_Instance, instanceOrthancId, seriesOrthancId); + cache->AddParentLink(Orthanc::ResourceType_Series, seriesOrthancId, studyOrthancId); + cache->AddParentLink(Orthanc::ResourceType_Study, studyOrthancId, patientOrthancId); + + IAuthorizationParser::AccessedResources accesses; + AssociativeArray noGetArguments(0, NULL, NULL, false); + + accesses.clear(); + parser.Parse(accesses, "/studies/22222222-22222222-22222222-22222222-22222222/", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/studies/22222222-22222222-22222222-22222222-22222222/instances", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/studies/22222222-22222222-22222222-22222222-22222222/archive", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/osimis-viewer/studies/22222222-22222222-22222222-22222222-22222222/archive", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/series/33333333-33333333-33333333-33333333-33333333/", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/series/33333333-33333333-33333333-33333333-33333333/media", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/series/33333333-33333333-33333333-33333333-33333333/modify", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/web-viewer/series/33333333-33333333-33333333-33333333-33333333", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/osimis-viewer/series/33333333-33333333-33333333-33333333-33333333", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/instances/44444444-44444444-44444444-44444444-44444444/file", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Instance, instanceOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/web-viewer/instances/jpeg95-44444444-44444444-44444444-44444444-44444444_0", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Instance, instanceOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/osimis-viewer/images/44444444-44444444-44444444-44444444-44444444/0/high-quality", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Instance, instanceOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/system", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_System, "/system")); + + + ///////////////////////// dicom-web + accesses.clear(); + parser.Parse(accesses, "/dicom-web/studies/2.2", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/dicom-web/studies/2.2/series/3.3", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/dicom-web/studies/2.2/series/3.3/instances/4.4", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Instance, instanceOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + accesses.clear(); + parser.Parse(accesses, "/dicom-web/studies/2.2/series/3.3/instances/4.4/frames/0", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Instance, instanceOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + + { + accesses.clear(); + const char* getKeys[] = {"0020000D"}; + const char* getValues[] = {"2.2"}; + AssociativeArray getArguments(1, getKeys, getValues, false); + parser.Parse(accesses, "/dicom-web/studies", getArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + } + { + accesses.clear(); + const char* getKeys[] = {"0020000D", "0020000E"}; + const char* getValues[] = {"2.2", "3.3"}; + AssociativeArray getArguments(2, getKeys, getValues, false); + parser.Parse(accesses, "/dicom-web/series", getArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + } + { + accesses.clear(); + const char* getKeys[] = {"0020000D", "00080018", "0020000E"}; + const char* getValues[] = {"2.2", "4.4", "3.3", }; + AssociativeArray getArguments(3, getKeys, getValues, false); + parser.Parse(accesses, "/dicom-web/studies", getArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Instance, instanceOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Series, seriesOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Study, studyOrthancId)); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + } + { + accesses.clear(); + const char* getKeys[] = {"00100010"}; + const char* getValues[] = {"PATIENT.1"}; + AssociativeArray getArguments(1, getKeys, getValues, false); + parser.Parse(accesses, "/dicom-web/studies", getArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_Patient, patientOrthancId)); + } + + { // qido with no arguments = search all => system resource + accesses.clear(); + parser.Parse(accesses, "/dicom-web/studies", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_System, "/dicom-web/studies")); + } + + accesses.clear(); + parser.Parse(accesses, "/dicom-web/servers", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_System, "/dicom-web/servers")); + + accesses.clear(); + parser.Parse(accesses, "/dicom-web/info", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_System, "/dicom-web/info")); + + accesses.clear(); + parser.Parse(accesses, "/dicom-web/servers/test/qido", noGetArguments.GetMap()); + ASSERT_TRUE(IsAccessing(accesses, AccessLevel_System, "/dicom-web/servers/test/qido")); + +} +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}