Mercurial > hg > orthanc-authorization
changeset 109:7381a7674b36
wip: adding labels
author | Alain Mazy <am@osimis.io> |
---|---|
date | Fri, 18 Aug 2023 12:08:49 +0200 |
parents | 68ce6fd8b22a |
children | aa56dcf599b9 |
files | CMakeLists.txt Plugin/AccessedResource.cpp Plugin/AccessedResource.h Plugin/AuthorizationParserBase.cpp Plugin/AuthorizationParserBase.h Plugin/AuthorizationWebService.cpp Plugin/AuthorizationWebService.h Plugin/BaseAuthorizationService.h Plugin/CachedAuthorizationService.cpp Plugin/CachedAuthorizationService.h Plugin/DefaultAuthorizationParser.cpp Plugin/IAuthorizationService.h Plugin/OrthancResource.cpp Plugin/OrthancResource.h Plugin/Plugin.cpp Plugin/ResourceHierarchyCache.cpp Plugin/ResourceHierarchyCache.h Resources/Orthanc/CMake/Compiler.cmake Resources/Orthanc/CMake/DownloadOrthancFramework.cmake |
diffstat | 19 files changed, 332 insertions(+), 96 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Mon Aug 14 10:25:40 2023 +0200 +++ b/CMakeLists.txt Fri Aug 18 12:08:49 2023 +0200 @@ -25,8 +25,9 @@ set(ORTHANC_FRAMEWORK_VERSION "mainline") set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") else() - set(ORTHANC_FRAMEWORK_VERSION "1.11.3") - set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") + # TODO: switch to 1.12.2 once available + set(ORTHANC_FRAMEWORK_VERSION "mainline") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") endif() # Parameters of the build @@ -170,6 +171,8 @@ ${PLUGIN_SOURCES} ) +DefineSourceBasenameForTarget(OrthancAuthorization) + add_dependencies(OrthancAuthorization AutogeneratedTarget) message("Setting the version of the plugin to ${ORTHANC_PLUGIN_VERSION}")
--- a/Plugin/AccessedResource.cpp Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/AccessedResource.cpp Fri Aug 18 12:08:49 2023 +0200 @@ -24,15 +24,17 @@ { AccessedResource::AccessedResource(AccessLevel level, const std::string& orthancId, - const std::string& dicomUid) : + const std::string& dicomUid, + const std::set<std::string>& labels) : level_(level), orthancId_(orthancId), - dicomUid_(dicomUid) + dicomUid_(dicomUid), + labels_(labels) { if (level_ == AccessLevel_System && - !dicomUid.empty()) + (!dicomUid.empty() || !labels.empty())) { - // The "DICOM UID" makes no sense for custom Orthanc URIs + // The "DICOM UID" and labels make no sense for custom Orthanc URIs throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } } @@ -40,9 +42,11 @@ AccessedResource::AccessedResource(Orthanc::ResourceType level, const std::string& orthancId, - const std::string& dicomUid) : + const std::string& dicomUid, + const std::set<std::string>& labels) : orthancId_(orthancId), - dicomUid_(dicomUid) + dicomUid_(dicomUid), + labels_(labels) { switch (level) { @@ -79,4 +83,17 @@ return dicomUid_; } } + + const std::set<std::string>& AccessedResource::GetLabels() const + { + if (level_ == AccessLevel_System) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return labels_; + } + } + }
--- a/Plugin/AccessedResource.h Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/AccessedResource.h Fri Aug 18 12:08:49 2023 +0200 @@ -19,6 +19,7 @@ #pragma once #include "Enumerations.h" +#include <set> namespace OrthancPlugins { @@ -28,15 +29,19 @@ AccessLevel level_; std::string orthancId_; std::string dicomUid_; + std::set<std::string> labels_; public: AccessedResource(AccessLevel level, const std::string& orthancId, - const std::string& dicomUid); + const std::string& dicomUid, + const std::set<std::string>& labels + ); AccessedResource(Orthanc::ResourceType level, const std::string& orthancId, - const std::string& dicomUid); + const std::string& dicomUid, + const std::set<std::string>& labels); AccessLevel GetLevel() const { @@ -49,5 +54,7 @@ } const std::string& GetDicomUid() const; + + const std::set<std::string>& GetLabels() const; }; }
--- a/Plugin/AuthorizationParserBase.cpp Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/AuthorizationParserBase.cpp Fri Aug 18 12:08:49 2023 +0200 @@ -24,13 +24,14 @@ { void AuthorizationParserBase::AddResourceInternal(AccessedResources& target, Orthanc::ResourceType level, - const std::string& orthancId) + const std::string& orthancId, + const std::set<std::string>& labels) { std::string dicomUid; if (resourceHierarchy_->LookupDicomUid(dicomUid, level, orthancId)) { - target.push_back(AccessedResource(level, orthancId, dicomUid)); + target.push_back(AccessedResource(level, orthancId, dicomUid, labels)); } } @@ -44,10 +45,19 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } - AddResourceInternal(target, Orthanc::ResourceType_Patient, patient); - AddResourceInternal(target, Orthanc::ResourceType_Study, study); - AddResourceInternal(target, Orthanc::ResourceType_Series, series); - AddResourceInternal(target, Orthanc::ResourceType_Instance, orthancId); + std::set<std::string> labels; + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Patient, patient)); + AddResourceInternal(target, Orthanc::ResourceType_Patient, patient, labels); + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Study, study)); + AddResourceInternal(target, Orthanc::ResourceType_Study, study, labels); + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Series, series)); + AddResourceInternal(target, Orthanc::ResourceType_Series, series, labels); + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Instance, orthancId)); + AddResourceInternal(target, Orthanc::ResourceType_Instance, orthancId, labels); } @@ -60,9 +70,16 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } - AddResourceInternal(target, Orthanc::ResourceType_Patient, patient); - AddResourceInternal(target, Orthanc::ResourceType_Study, study); - AddResourceInternal(target, Orthanc::ResourceType_Series, orthancId); + std::set<std::string> labels; + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Patient, patient)); + AddResourceInternal(target, Orthanc::ResourceType_Patient, patient, labels); + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Study, study)); + AddResourceInternal(target, Orthanc::ResourceType_Study, study, labels); + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Series, orthancId)); + AddResourceInternal(target, Orthanc::ResourceType_Series, orthancId, labels); } @@ -75,15 +92,23 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } - AddResourceInternal(target, Orthanc::ResourceType_Patient, patient); - AddResourceInternal(target, Orthanc::ResourceType_Study, orthancId); + std::set<std::string> labels; + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Patient, patient)); + AddResourceInternal(target, Orthanc::ResourceType_Patient, patient, labels); + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Study, orthancId)); + AddResourceInternal(target, Orthanc::ResourceType_Study, orthancId, labels); } void AuthorizationParserBase::AddOrthancPatient(AccessedResources& target, const std::string& orthancId) { - AddResourceInternal(target, Orthanc::ResourceType_Patient, orthancId); + std::set<std::string> labels; + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Patient, orthancId)); + AddResourceInternal(target, Orthanc::ResourceType_Patient, orthancId, labels); } @@ -98,8 +123,13 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } - AddResourceInternal(target, Orthanc::ResourceType_Patient, patient); - target.push_back(AccessedResource(Orthanc::ResourceType_Study, study, studyDicomUid)); + std::set<std::string> labels; + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Patient, patient)); + AddResourceInternal(target, Orthanc::ResourceType_Patient, patient, labels); + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Study, study)); + target.push_back(AccessedResource(Orthanc::ResourceType_Study, study, studyDicomUid, labels)); } void AuthorizationParserBase::AddDicomPatient(AccessedResources& target, @@ -112,7 +142,10 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } - AddResourceInternal(target, Orthanc::ResourceType_Patient, patient); + std::set<std::string> labels; + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Patient, patient)); + AddResourceInternal(target, Orthanc::ResourceType_Patient, patient, labels); } void AuthorizationParserBase::AddDicomSeries(AccessedResources& target, @@ -126,8 +159,12 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } + std::set<std::string> labels; + AddDicomStudy(target, studyDicomUid); - target.push_back(AccessedResource(Orthanc::ResourceType_Series, series, seriesDicomUid)); + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Series, series)); + target.push_back(AccessedResource(Orthanc::ResourceType_Series, series, seriesDicomUid, labels)); } @@ -144,8 +181,12 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } + std::set<std::string> labels; + AddDicomSeries(target, studyDicomUid, seriesDicomUid); - target.push_back(AccessedResource(Orthanc::ResourceType_Instance, instance, instanceDicomUid)); + + resourceHierarchy_->GetLabels(labels, OrthancResource(Orthanc::ResourceType_Instance, instance)); + target.push_back(AccessedResource(Orthanc::ResourceType_Instance, instance, instanceDicomUid, labels)); }
--- a/Plugin/AuthorizationParserBase.h Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/AuthorizationParserBase.h Fri Aug 18 12:08:49 2023 +0200 @@ -37,7 +37,8 @@ void AddResourceInternal(AccessedResources& target, Orthanc::ResourceType level, - const std::string& orthancId); + const std::string& orthancId, + const std::set<std::string>& labels); protected: void AddOrthancInstance(AccessedResources& target,
--- a/Plugin/AuthorizationWebService.cpp Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/AuthorizationWebService.cpp Fri Aug 18 12:08:49 2023 +0200 @@ -24,12 +24,16 @@ #include <Toolbox.h> #include <HttpClient.h> #include <algorithm> +#include "SerializationToolbox.h" namespace OrthancPlugins { static const char* GRANTED = "granted"; static const char* VALIDITY = "validity"; static const char* PERMISSIONS = "permissions"; + static const char* AUTHORIZED_LABELS = "authorized-labels"; + static const char* FORBIDDEN_LABELS = "forbidden-labels"; + static const char* USER_NAME = "name"; bool AuthorizationWebService::IsGrantedInternal(unsigned int& validity, @@ -89,6 +93,11 @@ body["server-id"] = Json::nullValue; } + if (access.GetLabels().size() > 0) + { + Orthanc::SerializationToolbox::WriteSetOfStrings(body, access.GetLabels(), "labels"); + } + Orthanc::WebServiceParameters authWebservice; if (!username_.empty()) @@ -315,7 +324,7 @@ bool AuthorizationWebService::GetUserProfileInternal(unsigned int& validity, - Json::Value& profile /* out */, + UserProfile& profile /* out */, const Token* token, const std::string& tokenValue) { @@ -361,15 +370,52 @@ authClient.AddHeader("Expect", ""); authClient.SetTimeout(10); - authClient.ApplyAndThrowException(profile); + Json::Value jsonProfile; + authClient.ApplyAndThrowException(jsonProfile); - if (profile.isMember("validity")) + if (jsonProfile.type() != Json::objectValue || + !jsonProfile.isMember(PERMISSIONS) || + !jsonProfile.isMember(VALIDITY) || + !jsonProfile.isMember(AUTHORIZED_LABELS) || + !jsonProfile.isMember(FORBIDDEN_LABELS) || + !jsonProfile.isMember(USER_NAME) || + jsonProfile[PERMISSIONS].type() != Json::arrayValue || + jsonProfile[AUTHORIZED_LABELS].type() != Json::arrayValue || + jsonProfile[FORBIDDEN_LABELS].type() != Json::arrayValue || + jsonProfile[VALIDITY].type() != Json::intValue || + jsonProfile[USER_NAME].type() != Json::stringValue) { - validity = profile["validity"].asInt(); + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, + "Syntax error in the result of the Auth Web service, the format of the UserProfile is invalid"); + } + + validity = jsonProfile[VALIDITY].asUInt(); + + profile.name = jsonProfile[USER_NAME].asString(); + + for (Json::ArrayIndex i = 0; i < jsonProfile[PERMISSIONS].size(); ++i) + { + profile.permissions.insert(jsonProfile[PERMISSIONS][i].asString()); + } + for (Json::ArrayIndex i = 0; i < jsonProfile[AUTHORIZED_LABELS].size(); ++i) + { + profile.authorizedLabels.insert(jsonProfile[AUTHORIZED_LABELS][i].asString()); } - else + for (Json::ArrayIndex i = 0; i < jsonProfile[FORBIDDEN_LABELS].size(); ++i) + { + profile.forbiddenLabels.insert(jsonProfile[FORBIDDEN_LABELS][i].asString()); + } + + if (profile.authorizedLabels.size() > 0 && profile.forbiddenLabels.size() > 0) { - validity = 0; + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, + "Syntax error in the result of the Auth Web service, the UserProfile can not contain both authorized and forbidden labels"); + } + + if (profile.authorizedLabels.size() == 0 && profile.forbiddenLabels.size() == 0) + { + LOG(WARNING) << "The UserProfile does not contain any authorized or forbidden labels, assuming the user has access to all data (equivalent to \"authorized_labels\": [\"*\"]) !"; + profile.authorizedLabels.insert("*"); } return true; @@ -385,27 +431,15 @@ const Token* token, const std::string& tokenValue) { - Json::Value profile; + UserProfile profile; if (GetUserProfileInternal(validity, profile, token, tokenValue)) { - if (profile.type() != Json::objectValue || - !profile.isMember(PERMISSIONS) || - !profile.isMember(VALIDITY) || - profile[PERMISSIONS].type() != Json::arrayValue || - profile[VALIDITY].type() != Json::intValue) + std::set<std::string>& permissions = profile.permissions; + for (std::set<std::string>::const_iterator it = permissions.begin(); it != permissions.end(); ++it) { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, - "Syntax error in the result of the Web service"); - } - - validity = profile[VALIDITY].asUInt(); - - Json::Value& permissions = profile[PERMISSIONS]; - for (Json::ArrayIndex i = 0; i < permissions.size(); ++i) - { - if (permission == permissions[i].asString()) + if (permission == *it) { return true; }
--- a/Plugin/AuthorizationWebService.h Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/AuthorizationWebService.h Fri Aug 18 12:08:49 2023 +0200 @@ -42,7 +42,7 @@ const std::string& tokenValue) ORTHANC_OVERRIDE; virtual bool GetUserProfileInternal(unsigned int& validity, - Json::Value& profile /* out */, + UserProfile& profile /* out */, const Token* token, const std::string& tokenValue) ORTHANC_OVERRIDE;
--- a/Plugin/BaseAuthorizationService.h Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/BaseAuthorizationService.h Fri Aug 18 12:08:49 2023 +0200 @@ -36,7 +36,7 @@ const std::string& tokenValue) = 0; virtual bool GetUserProfileInternal(unsigned int& validity, - Json::Value& profile /* out */, + UserProfile& profile /* out */, const Token* token, const std::string& tokenValue) = 0; @@ -67,7 +67,7 @@ } virtual bool GetUserProfile(unsigned int& validity, - Json::Value& profile /* out */, + UserProfile& profile /* out */, const Token& token, const std::string& tokenValue) { @@ -75,7 +75,7 @@ } virtual bool GetAnonymousUserProfile(unsigned int& validity /* out */, - Json::Value& profile /* out */) + UserProfile& profile /* out */) { return GetUserProfileInternal(validity, profile, NULL, ""); }
--- a/Plugin/CachedAuthorizationService.cpp Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/CachedAuthorizationService.cpp Fri Aug 18 12:08:49 2023 +0200 @@ -113,7 +113,7 @@ bool CachedAuthorizationService::GetUserProfileInternal(unsigned int& validity, - Json::Value& profile /* out */, + UserProfile& profile /* out */, const Token* token, const std::string& tokenValue) {
--- a/Plugin/CachedAuthorizationService.h Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/CachedAuthorizationService.h Fri Aug 18 12:08:49 2023 +0200 @@ -52,7 +52,7 @@ const std::string& tokenValue) ORTHANC_OVERRIDE; virtual bool GetUserProfileInternal(unsigned int& validity, - Json::Value& profile /* out */, + UserProfile& profile /* out */, const Token* token, const std::string& tokenValue) ORTHANC_OVERRIDE;
--- a/Plugin/DefaultAuthorizationParser.cpp Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/DefaultAuthorizationParser.cpp Fri Aug 18 12:08:49 2023 +0200 @@ -192,7 +192,9 @@ s = s.substr(0, s.length() - 1); } - target.push_back(AccessedResource(AccessLevel_System, s, "")); + std::set<std::string> labels; + + target.push_back(AccessedResource(AccessLevel_System, s, "", labels)); return true; } }
--- a/Plugin/IAuthorizationService.h Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/IAuthorizationService.h Fri Aug 18 12:08:49 2023 +0200 @@ -24,6 +24,7 @@ #include <orthanc/OrthancCPlugin.h> #include <boost/noncopyable.hpp> #include <json/json.h> +#include <set> namespace OrthancPlugins { @@ -52,6 +53,14 @@ std::string tokenType; }; + struct UserProfile + { + std::string name; + std::set<std::string> permissions; + std::set<std::string> authorizedLabels; + std::set<std::string> forbiddenLabels; + }; + virtual ~IAuthorizationService() { } @@ -67,12 +76,12 @@ const AccessedResource& access) = 0; virtual bool GetUserProfile(unsigned int& validity /* out */, - Json::Value& profile /* out */, + UserProfile& profile /* out */, const Token& token, const std::string& tokenValue) = 0; virtual bool GetAnonymousUserProfile(unsigned int& validity /* out */, - Json::Value& profile /* out */) = 0; + UserProfile& profile /* out */) = 0; virtual bool HasUserPermission(unsigned int& validity /* out */, const std::set<std::string>& anyOfPermissions,
--- a/Plugin/OrthancResource.cpp Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/OrthancResource.cpp Fri Aug 18 12:08:49 2023 +0200 @@ -20,6 +20,8 @@ #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" +static const char* LABELS_KEY = "Labels"; + namespace OrthancPlugins { void OrthancResource::GetDicomUidInternal(std::string& result, @@ -157,7 +159,8 @@ bool OrthancResource::GetHierarchy(std::string& dicomUid /* out */, OrthancResource& parent /* out */, - std::list<OrthancResource>& children /* out */) const + std::list<OrthancResource>& children /* out */, + std::set<std::string>& labels) const { Json::Value content; @@ -235,7 +238,24 @@ children.push_back(OrthancResource(childrenType, child.asString())); } } - + + labels.clear(); + if (content.isMember(LABELS_KEY) || + content[LABELS_KEY].type() != Json::arrayValue) + { + for (Json::Value::ArrayIndex i = 0; i < content[LABELS_KEY].size(); i++) + { + const Json::Value& label = content[LABELS_KEY][i]; + + if (label.type() != Json::stringValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin); + } + + labels.insert(label.asString()); + } + } + return true; }
--- a/Plugin/OrthancResource.h Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/OrthancResource.h Fri Aug 18 12:08:49 2023 +0200 @@ -66,7 +66,8 @@ bool GetHierarchy(std::string& dicomUid /* out */, OrthancResource& parent /* out */, - std::list<OrthancResource>& children /* out */) const; + std::list<OrthancResource>& children /* out */, + std::set<std::string>& labels /* out */) const; static bool LookupOrthancId(std::string& result, Orthanc::ResourceType level,
--- a/Plugin/Plugin.cpp Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/Plugin.cpp Fri Aug 18 12:08:49 2023 +0200 @@ -22,7 +22,6 @@ #include "AuthorizationWebService.h" #include "PermissionParser.h" #include "MemoryCache.h" - #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" #include <Compatibility.h> // For std::unique_ptr<> @@ -88,9 +87,11 @@ { unsigned int validity; // ignored + // Allow GET accesses to unchecked resources/folders (usually static resources) + //////////////////////////////////////////////////////////////// + if (method == OrthancPluginHttpMethod_Get) { - // Allow GET accesses to static resources if (uncheckedResources_.find(uri) != uncheckedResources_.end()) { return 1; @@ -106,6 +107,9 @@ } } + // Extract auth tokens from headers and url get arguments + //////////////////////////////////////////////////////////////// + OrthancPlugins::AssociativeArray headers(headersCount, headersKeys, headersValues, false); OrthancPlugins::AssociativeArray getArguments(getArgumentsCount, getArgumentsKeys, getArgumentsValues, true); @@ -136,10 +140,11 @@ } } - // check if the user permissions grants him access + // Based on the tokens, check if the user has access based on its permissions and the mapping between urls and permissions + //////////////////////////////////////////////////////////////// + if (permissionParser_.get() != NULL && authorizationService_.get() != NULL) - // && uncheckedLevels_.find(OrthancPlugins::AccessLevel_UserPermissions) == uncheckedLevels_.end()) { std::set<std::string> requiredPermissions; std::string matchedPattern; @@ -168,6 +173,7 @@ LOG(INFO) << msg; if (authorizationService_->HasUserPermission(validity, requiredPermissions, authTokens[i].GetToken(), authTokens[i].GetValue())) { + // TODO: check labels permissions LOG(INFO) << msg << " -> granted"; return 1; } @@ -179,6 +185,10 @@ } } } + + + // + if (authorizationParser_.get() != NULL && authorizationService_.get() != NULL) { @@ -508,7 +518,7 @@ for (std::set<OrthancPlugins::Token>::const_iterator token = tokens_.begin(); token != tokens_.end(); ++token) { - Json::Value profile; + OrthancPlugins::IAuthorizationService::UserProfile profile; std::string value; @@ -532,7 +542,23 @@ unsigned int validity; // not used if (authorizationService_->GetUserProfile(validity, profile, *token, value)) { - OrthancPlugins::AnswerJson(profile, output); + Json::Value jsonProfile; + jsonProfile["name"] = profile.name; + jsonProfile["permissions"] = Json::arrayValue; + for (std::set<std::string>::const_iterator it = profile.permissions.begin(); it != profile.permissions.end(); ++it) + { + jsonProfile["permissions"].append(*it); + } + for (std::set<std::string>::const_iterator it = profile.authorizedLabels.begin(); it != profile.authorizedLabels.end(); ++it) + { + jsonProfile["authorized-labels"].append(*it); + } + for (std::set<std::string>::const_iterator it = profile.forbiddenLabels.begin(); it != profile.forbiddenLabels.end(); ++it) + { + jsonProfile["forbidden-labels"].append(*it); + } + + OrthancPlugins::AnswerJson(jsonProfile, output); return; } }
--- a/Plugin/ResourceHierarchyCache.cpp Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/ResourceHierarchyCache.cpp Fri Aug 18 12:08:49 2023 +0200 @@ -22,6 +22,7 @@ #include <OrthancException.h> #include <boost/lexical_cast.hpp> +#include <Toolbox.h> namespace OrthancPlugins { @@ -43,6 +44,63 @@ cache_->Store(ComputeKey(child), parent.GetIdentifier(), 0 /* no expiration */); } + void ResourceHierarchyCache::GetLabels(std::set<std::string>& labels, + const OrthancResource& resource) + { + labels.clear(); + + std::string key = ComputeKey(resource); + + std::string serializedLabels; + if (!labels_->Retrieve(serializedLabels, key)) + { + // The labels were not already stored in the cache or they have expired + OrthancResource parent; + UpdateResourceFromOrthanc(parent, labels, resource); + } + else + { + Orthanc::Toolbox::SplitString(labels, serializedLabels, ','); + } + } + + + void ResourceHierarchyCache::UpdateResourceFromOrthanc(OrthancResource& parent, + std::set<std::string>& labels, + const OrthancResource& resource) + { + std::string key = ComputeKey(resource); + + // Not in the cache, reading the resource from the Orthanc store + std::string dicomUid; + std::list<OrthancResource> children; + + if (!resource.GetHierarchy(dicomUid, parent, children, labels)) + { + // The resource is non-existing (*) + return; + } + + orthancToDicom_->Store(key, dicomUid, 0 /* no expiration */); + dicomToOrthanc_->Store(ComputeKey(resource.GetLevel(), dicomUid), + resource.GetIdentifier(), 0 /* no expiration */); + std::string serializedLabels; + Orthanc::Toolbox::JoinStrings(serializedLabels, labels, ","); + labels_->Store(key, serializedLabels, 60); + + for (std::list<OrthancResource>::const_iterator + it = children.begin(); it != children.end(); ++it) + { + // Cache the relation of the resource with its children + LinkParent(*it, resource); + } + + if (parent.IsValid()) + { + LinkParent(resource, parent); + } + } + bool ResourceHierarchyCache::LookupParent(std::string& target, const OrthancResource& resource) @@ -55,31 +113,12 @@ return true; } - // Not in the cache, reading the resource from the Orthanc store - std::string dicomUid; OrthancResource parent; - std::list<OrthancResource> children; - - if (!resource.GetHierarchy(dicomUid, parent, children)) - { - // The resource is non-existing (*) - return false; - } - - orthancToDicom_->Store(key, dicomUid, 0 /* no expiration */); - dicomToOrthanc_->Store(ComputeKey(resource.GetLevel(), dicomUid), - resource.GetIdentifier(), 0 /* no expiration */); - - for (std::list<OrthancResource>::const_iterator - it = children.begin(); it != children.end(); ++it) - { - // Cache the relation of the resource with its children - LinkParent(*it, resource); - } + std::set<std::string> labels; + UpdateResourceFromOrthanc(parent, labels, resource); if (parent.IsValid()) { - LinkParent(resource, parent); target = parent.GetIdentifier(); return true; } @@ -95,7 +134,8 @@ ResourceHierarchyCache::ResourceHierarchyCache(ICacheFactory& factory) : cache_(factory.Create()), orthancToDicom_(factory.Create()), - dicomToOrthanc_(factory.Create()) + dicomToOrthanc_(factory.Create()), + labels_(factory.Create()) { if (cache_.get() == NULL) { @@ -113,6 +153,7 @@ std::string key = ComputeKey(level, identifier); cache_->Invalidate(key); orthancToDicom_->Invalidate(key); + labels_->Invalidate(key); }
--- a/Plugin/ResourceHierarchyCache.h Mon Aug 14 10:25:40 2023 +0200 +++ b/Plugin/ResourceHierarchyCache.h Fri Aug 18 12:08:49 2023 +0200 @@ -38,6 +38,7 @@ std::unique_ptr<ICache> cache_; // Maps resources to their parents std::unique_ptr<ICache> orthancToDicom_; std::unique_ptr<ICache> dicomToOrthanc_; + std::unique_ptr<ICache> labels_; std::string ComputeKey(Orthanc::ResourceType level, const std::string& identifier) const; @@ -60,6 +61,10 @@ return LookupParent(target, OrthancResource(level, identifier)); } + void UpdateResourceFromOrthanc(OrthancResource& parent, + std::set<std::string>& labels, + const OrthancResource& resource); + public: explicit ResourceHierarchyCache(ICacheFactory& factory); @@ -89,6 +94,9 @@ Orthanc::ResourceType level, const std::string& dicomUid); + void GetLabels(std::set<std::string>& labels, + const OrthancResource& resource); + #if BUILD_UNIT_TESTS == 1 FRIEND_TEST(DefaultAuthorizationParser, Parse); protected:
--- a/Resources/Orthanc/CMake/Compiler.cmake Mon Aug 14 10:25:40 2023 +0200 +++ b/Resources/Orthanc/CMake/Compiler.cmake Fri Aug 18 12:08:49 2023 +0200 @@ -1,8 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2022 Osimis S.A., Belgium -# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License @@ -237,7 +237,8 @@ if (DEFINED ENABLE_PROFILING AND ENABLE_PROFILING) - if (CMAKE_COMPILER_IS_GNUCXX) + if (CMAKE_COMPILER_IS_GNUCXX OR + CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg") @@ -262,3 +263,24 @@ # preceding batches. https://cmake.org/Bug/view.php?id=14874 set(CMAKE_CXX_ARCHIVE_APPEND "<CMAKE_AR> <LINK_FLAGS> q <TARGET> <OBJECTS>") endif() + + +# This function defines macro "__ORTHANC_FILE__" as a replacement to +# macro "__FILE__", as the latter leaks the full path of the source +# files in the binaries +# https://stackoverflow.com/questions/8487986/file-macro-shows-full-path +# https://twitter.com/wget42/status/1676877802375634944?s=20 +function(DefineSourceBasenameForTarget targetname) + # Microsoft Visual Studio is extremely slow if using + # "set_property()", we only enable this feature for gcc and clang + if (CMAKE_COMPILER_IS_GNUCXX OR + CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + get_target_property(source_files "${targetname}" SOURCES) + foreach(sourcefile ${source_files}) + get_filename_component(basename "${sourcefile}" NAME) + set_property( + SOURCE "${sourcefile}" APPEND + PROPERTY COMPILE_DEFINITIONS "__ORTHANC_FILE__=\"${basename}\"") + endforeach() + endif() +endfunction()
--- a/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake Mon Aug 14 10:25:40 2023 +0200 +++ b/Resources/Orthanc/CMake/DownloadOrthancFramework.cmake Fri Aug 18 12:08:49 2023 +0200 @@ -1,8 +1,8 @@ # Orthanc - A Lightweight, RESTful DICOM Store # Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics # Department, University Hospital of Liege, Belgium -# Copyright (C) 2017-2022 Osimis S.A., Belgium -# Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium # # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License @@ -154,6 +154,10 @@ set(ORTHANC_FRAMEWORK_MD5 "ede3de356493a8868545f8cb4b8bc8b5") elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.11.3") set(ORTHANC_FRAMEWORK_MD5 "f941c0f5771db7616e7b7961026a60e2") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.12.0") + set(ORTHANC_FRAMEWORK_MD5 "d32a0cde03b6eb603d8dd2b33d38bf1b") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.12.1") + set(ORTHANC_FRAMEWORK_MD5 "8a435140efc8ff4a01d8242f092f21de") # Below this point are development snapshots that were used to # release some plugin, before an official release of the Orthanc @@ -313,7 +317,7 @@ else() # Default case: Download from the official Web site set(ORTHANC_FRAMEMORK_FILENAME Orthanc-${ORTHANC_FRAMEWORK_VERSION}.tar.gz) - set(ORTHANC_FRAMEWORK_URL "http://orthanc.osimis.io/ThirdPartyDownloads/orthanc-framework/${ORTHANC_FRAMEMORK_FILENAME}") + set(ORTHANC_FRAMEWORK_URL "https://orthanc.uclouvain.be/third-party-downloads/orthanc-framework/${ORTHANC_FRAMEMORK_FILENAME}") endif() set(ORTHANC_FRAMEWORK_ARCHIVE "${CMAKE_SOURCE_DIR}/ThirdPartyDownloads/${ORTHANC_FRAMEMORK_FILENAME}")