# HG changeset patch # User Mark Poscablo # Date 1608058741 18000 # Node ID 208029732d510473cdf7921ec90544b7a06f6697 # Parent 79ef2b6d8e767f6f141958f744e1cb102758b698 New config option "DeidentifyDimseQueryLogs" diff -r 79ef2b6d8e76 -r 208029732d51 OrthancServer/Resources/Configuration.json --- a/OrthancServer/Resources/Configuration.json Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancServer/Resources/Configuration.json Tue Dec 15 13:59:01 2020 -0500 @@ -630,4 +630,15 @@ // option to "0" doesn't call mallopt()", which was the behavior of // Orthanc <= 1.8.1. "MallocArenaMax" : 5 + + // Deidentify/anonymize the contents of C-Find, C-Get, and C-Move + // queries submitted to Orthanc according to Table E.1-1 of the + // DICOM standard + "DeidentifyDimseQueryLogs": true, + + // If DeidentifyDimseQueryLogs is true, this sets the DICOM standard + // to follow for the deintification/anonymization of the query + // contents. Possible values are those that are specified in the + // definition of Orthanc::StringToDicomVersion + "DeidentifyDimseQueryLogsDicomVersion": "2017c", } diff -r 79ef2b6d8e76 -r 208029732d51 OrthancServer/Sources/OrthancFindRequestHandler.cpp --- a/OrthancServer/Sources/OrthancFindRequestHandler.cpp Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancServer/Sources/OrthancFindRequestHandler.cpp Tue Dec 15 13:59:01 2020 -0500 @@ -639,7 +639,7 @@ { CLOG(INFO, DICOM) << " (" << query.GetElement(i).GetTag().Format() << ") " << FromDcmtkBridge::GetTagName(query.GetElement(i)) - << " = " << query.GetElement(i).GetValue().GetContent(); + << " = " << context_.GetDeidentifiedQueryContent(query.GetElement(i)); } } diff -r 79ef2b6d8e76 -r 208029732d51 OrthancServer/Sources/OrthancGetRequestHandler.cpp --- a/OrthancServer/Sources/OrthancGetRequestHandler.cpp Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancServer/Sources/OrthancGetRequestHandler.cpp Tue Dec 15 13:59:01 2020 -0500 @@ -507,7 +507,7 @@ { CLOG(INFO, DICOM) << " (" << query.GetElement(i).GetTag().Format() << ") " << FromDcmtkBridge::GetTagName(query.GetElement(i)) - << " = " << query.GetElement(i).GetValue().GetContent(); + << " = " << context_.GetDeidentifiedQueryContent(query.GetElement(i)); } } } diff -r 79ef2b6d8e76 -r 208029732d51 OrthancServer/Sources/OrthancMoveRequestHandler.cpp --- a/OrthancServer/Sources/OrthancMoveRequestHandler.cpp Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancServer/Sources/OrthancMoveRequestHandler.cpp Tue Dec 15 13:59:01 2020 -0500 @@ -344,7 +344,7 @@ { CLOG(INFO, DICOM) << " (" << query.GetElement(i).GetTag().Format() << ") " << FromDcmtkBridge::GetTagName(query.GetElement(i)) - << " = " << query.GetElement(i).GetValue().GetContent(); + << " = " << context_.GetDeidentifiedQueryContent(query.GetElement(i)); } } } diff -r 79ef2b6d8e76 -r 208029732d51 OrthancServer/Sources/ServerContext.cpp --- a/OrthancServer/Sources/ServerContext.cpp Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancServer/Sources/ServerContext.cpp Tue Dec 15 13:59:01 2020 -0500 @@ -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,9 @@ isExecuteLuaEnabled_(false), overwriteInstances_(false), dcmtkTranscoder_(new DcmtkTranscoder), - isIngestTranscoding_(false) + isIngestTranscoding_(false), + deidentifyDimseQueryLogs_(false), + deidentifyDimseQueryLogsDicomVersion_(DicomVersion_2017c) { try { @@ -318,6 +322,22 @@ isIngestTranscoding_ = false; LOG(INFO) << "Automated transcoding of incoming DICOM instances is disabled"; } + + if (lock.GetConfiguration().GetBooleanParameter("DeidentifyDimseQueryLogs", false)) + { + deidentifyDimseQueryLogs_ = true; + CLOG(INFO, DICOM) << "Deidentification of DIMSE query log contents is enabled"; + + deidentifyDimseQueryLogsDicomVersion_ = StringToDicomVersion( + lock.GetConfiguration().GetStringParameter("DeidentifyDimseQueryLogsDicomVersion", "2017c")); + CLOG(INFO, DICOM) << "Version of DICOM standard used for deidentification is " + << EnumerationToString(deidentifyDimseQueryLogsDicomVersion_); + } + else + { + deidentifyDimseQueryLogs_ = false; + CLOG(INFO, DICOM) << "Deidentification of DIMSE query log contents is disabled"; + } } jobsEngine_.SetThreadSleep(unitTesting ? 20 : 200); @@ -326,6 +346,11 @@ changeThread_ = boost::thread(ChangeThread, this, (unitTesting ? 20 : 100)); dynamic_cast(*dcmtkTranscoder_).SetLossyQuality(lossyQuality); + + if (deidentifyDimseQueryLogs_) + { + logsDeidentifierRules_.SetupAnonymization(deidentifyDimseQueryLogsDicomVersion_); + } } catch (OrthancException&) { @@ -1627,4 +1652,22 @@ return false; } } + + const std::string& ServerContext::GetDeidentifiedQueryContent(const DicomElement &element) const + { + static const std::string redactedContent = "*** POTENTIAL PHI ***"; + + const DicomTag& tag = element.GetTag(); + if (deidentifyDimseQueryLogs_ && ( + logsDeidentifierRules_.IsCleared(tag) || + logsDeidentifierRules_.IsRemoved(tag) || + logsDeidentifierRules_.IsReplaced(tag))) + { + return redactedContent; + } + else + { + return element.GetValue().GetContent(); + } + } } diff -r 79ef2b6d8e76 -r 208029732d51 OrthancServer/Sources/ServerContext.h --- a/OrthancServer/Sources/ServerContext.h Thu Dec 17 10:55:32 2020 +0100 +++ b/OrthancServer/Sources/ServerContext.h Tue Dec 15 13:59:01 2020 -0500 @@ -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 + DicomModification logsDeidentifierRules_; + bool deidentifyDimseQueryLogs_; + DicomVersion deidentifyDimseQueryLogsDicomVersion_; + public: class DicomCacheLocker : public boost::noncopyable { @@ -500,5 +508,7 @@ { return transcodeDicomProtocol_; } + + const std::string& GetDeidentifiedQueryContent(const DicomElement& element) const; }; }