# HG changeset patch # User Sebastien Jodogne # Date 1608201768 -3600 # Node ID 4770ac7287ecc7c54d8f55b2a9a0f4d0ff7c0cb4 # Parent 79ef2b6d8e767f6f141958f744e1cb102758b698# Parent b002f9abe802b3e93d789ddcc08cbbd331d019b1 integration varian->mainline diff -r 79ef2b6d8e76 -r 4770ac7287ec NEWS --- a/NEWS Thu Dec 17 10:55:32 2020 +0100 +++ b/NEWS Thu Dec 17 11:42:48 2020 +0100 @@ -8,6 +8,9 @@ * New config option "MallocArenaMax" to control memory usage on GNU/Linux * Explicit error log if trying to load a 32bit (resp. 64bit) plugin into a 64bit (resp. 32bit) version of Orthanc +* New configuration options (contributions by Varian): + - "DeidentifyLogs" to remove patient identification from the logs (C-GET, C-MOVE, C-FIND) + - "DeidentifyLogsDicomVersion" to specify the deidentification rules for the logs REST API -------- diff -r 79ef2b6d8e76 -r 4770ac7287ec OrthancFramework/Sources/Enumerations.cpp --- a/OrthancFramework/Sources/Enumerations.cpp Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancFramework/Sources/Enumerations.cpp Thu Dec 17 11:42:48 2020 +0100 @@ -1608,7 +1608,8 @@ } else { - throw OrthancException(ErrorCode_ParameterOutOfRange); + throw OrthancException(ErrorCode_ParameterOutOfRange, + "Unknown specific version of the DICOM standard: " + version); } } diff -r 79ef2b6d8e76 -r 4770ac7287ec OrthancServer/Resources/Configuration.json --- a/OrthancServer/Resources/Configuration.json Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancServer/Resources/Configuration.json Thu Dec 17 11:42:48 2020 +0100 @@ -629,5 +629,16 @@ // threads share "N" memory pools (known as "arenas"). Setting this // option to "0" doesn't call mallopt()", which was the behavior of // Orthanc <= 1.8.1. - "MallocArenaMax" : 5 + "MallocArenaMax" : 5, + + // Deidentify/anonymize the contents of the logs (notably C-Find, + // C-Get, and C-Move queries submitted to Orthanc) according to + // Table E.1-1 of the DICOM standard (new in Orthanc 1.8.2) + "DeidentifyLogs" : true, + + // If "DeidentifyLogs" is true, this sets the DICOM standard + // to follow for the deidentification/anonymization of the query + // contents. Possible values are those that are specified in the + // definition of Orthanc::StringToDicomVersion (new in Orthanc 1.8.2) + "DeidentifyLogsDicomVersion" : "2017c" } diff -r 79ef2b6d8e76 -r 4770ac7287ec OrthancServer/Sources/OrthancFindRequestHandler.cpp --- a/OrthancServer/Sources/OrthancFindRequestHandler.cpp Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancServer/Sources/OrthancFindRequestHandler.cpp Thu Dec 17 11:42:48 2020 +0100 @@ -639,7 +639,7 @@ { CLOG(INFO, DICOM) << " (" << query.GetElement(i).GetTag().Format() << ") " << FromDcmtkBridge::GetTagName(query.GetElement(i)) - << " = " << query.GetElement(i).GetValue().GetContent(); + << " = " << context_.GetDeidentifiedContent(query.GetElement(i)); } } diff -r 79ef2b6d8e76 -r 4770ac7287ec OrthancServer/Sources/OrthancGetRequestHandler.cpp --- a/OrthancServer/Sources/OrthancGetRequestHandler.cpp Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancServer/Sources/OrthancGetRequestHandler.cpp Thu Dec 17 11:42:48 2020 +0100 @@ -507,7 +507,7 @@ { CLOG(INFO, DICOM) << " (" << query.GetElement(i).GetTag().Format() << ") " << FromDcmtkBridge::GetTagName(query.GetElement(i)) - << " = " << query.GetElement(i).GetValue().GetContent(); + << " = " << context_.GetDeidentifiedContent(query.GetElement(i)); } } } @@ -517,8 +517,14 @@ **/ const DicomValue* levelTmp = input.TestAndGetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL); + if (levelTmp == NULL || + levelTmp->IsNull() || + levelTmp->IsBinary()) + { + throw OrthancException(ErrorCode_BadRequest, + "C-GET request without the tag 0008,0052 (QueryRetrieveLevel)"); + } - assert(levelTmp != NULL); ResourceType level = StringToResourceType(levelTmp->GetContent().c_str()); diff -r 79ef2b6d8e76 -r 4770ac7287ec OrthancServer/Sources/OrthancMoveRequestHandler.cpp --- a/OrthancServer/Sources/OrthancMoveRequestHandler.cpp Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancServer/Sources/OrthancMoveRequestHandler.cpp Thu Dec 17 11:42:48 2020 +0100 @@ -344,7 +344,7 @@ { CLOG(INFO, DICOM) << " (" << query.GetElement(i).GetTag().Format() << ") " << FromDcmtkBridge::GetTagName(query.GetElement(i)) - << " = " << query.GetElement(i).GetValue().GetContent(); + << " = " << context_.GetDeidentifiedContent(query.GetElement(i)); } } } diff -r 79ef2b6d8e76 -r 4770ac7287ec OrthancServer/Sources/ServerContext.cpp --- a/OrthancServer/Sources/ServerContext.cpp Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancServer/Sources/ServerContext.cpp Thu Dec 17 11:42:48 2020 +0100 @@ -35,7 +35,9 @@ #include "ServerContext.h" #include "../../OrthancFramework/Sources/Cache/SharedArchive.h" +#include "../../OrthancFramework/Sources/DicomFormat/DicomElement.h" #include "../../OrthancFramework/Sources/DicomParsing/DcmtkTranscoder.h" +#include "../../OrthancFramework/Sources/DicomParsing/DicomModification.h" #include "../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h" #include "../../OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.h" #include "../../OrthancFramework/Sources/FileStorage/StorageAccessor.h" @@ -267,7 +269,8 @@ isExecuteLuaEnabled_(false), overwriteInstances_(false), dcmtkTranscoder_(new DcmtkTranscoder), - isIngestTranscoding_(false) + isIngestTranscoding_(false), + deidentifyLogs_(false) { try { @@ -318,6 +321,24 @@ isIngestTranscoding_ = false; LOG(INFO) << "Automated transcoding of incoming DICOM instances is disabled"; } + + if (lock.GetConfiguration().GetBooleanParameter("DeidentifyLogs", true)) + { + deidentifyLogs_ = true; + CLOG(INFO, DICOM) << "Deidentification of log contents (notably for DIMSE queries) is enabled"; + + DicomVersion version = StringToDicomVersion( + lock.GetConfiguration().GetStringParameter("DeidentifyLogsDicomVersion", "2017c")); + CLOG(INFO, DICOM) << "Version of DICOM standard used for deidentification is " + << EnumerationToString(version); + + logsDeidentifierRules_.SetupAnonymization(version); + } + else + { + deidentifyLogs_ = false; + CLOG(INFO, DICOM) << "Deidentification of log contents (notably for DIMSE queries) is disabled"; + } } jobsEngine_.SetThreadSleep(unitTesting ? 20 : 200); @@ -1627,4 +1648,22 @@ return false; } } + + const std::string& ServerContext::GetDeidentifiedContent(const DicomElement &element) const + { + static const std::string redactedContent = "*** POTENTIAL PHI ***"; + + const DicomTag& tag = element.GetTag(); + if (deidentifyLogs_ && ( + logsDeidentifierRules_.IsCleared(tag) || + logsDeidentifierRules_.IsRemoved(tag) || + logsDeidentifierRules_.IsReplaced(tag))) + { + return redactedContent; + } + else + { + return element.GetValue().GetContent(); + } + } } diff -r 79ef2b6d8e76 -r 4770ac7287ec OrthancServer/Sources/ServerContext.h --- a/OrthancServer/Sources/ServerContext.h Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancServer/Sources/ServerContext.h Thu Dec 17 11:42:48 2020 +0100 @@ -40,6 +40,8 @@ #include "ServerJobs/IStorageCommitmentFactory.h" #include "../../OrthancFramework/Sources/Cache/MemoryCache.h" +#include "../../OrthancFramework/Sources/DicomFormat/DicomElement.h" +#include "../../OrthancFramework/Sources/DicomParsing/DicomModification.h" #include "../../OrthancFramework/Sources/DicomParsing/IDicomTranscoder.h" @@ -245,6 +247,12 @@ size_t since, size_t limit); + // This DicomModification object is intended to be used as a + // "rules engine" when de-identifying logs for C-Find, C-Get, and + // C-Move queries (new in Orthanc 1.8.2) + DicomModification logsDeidentifierRules_; + bool deidentifyLogs_; + public: class DicomCacheLocker : public boost::noncopyable { @@ -500,5 +508,7 @@ { return transcodeDicomProtocol_; } + + const std::string& GetDeidentifiedContent(const DicomElement& element) const; }; }