# HG changeset patch # User Alain Mazy # Date 1752510216 -7200 # Node ID b340ccd0b22a76cc573f44edcb032efdcf52801e # Parent f1c8f36e0b87c4ee27c87463a4ab17ae917567d3 wip: audit-logs diff -r f1c8f36e0b87 -r b340ccd0b22a NEWS --- a/NEWS Mon Jul 14 11:40:39 2025 +0200 +++ b/NEWS Mon Jul 14 18:23:36 2025 +0200 @@ -1,9 +1,5 @@ -2025-07-14 - v 0.9.4 -==================== - -=> Minimum Orthanc version: 1.11.3 <= -=> Recommended SDK version: 1.12.4 <= -=> Minimum SDK version: 1.11.3 <= +Pending changes in the mainline +=============================== * New configuration "ExtraPermissions" to ADD new permissions to the default "Permissions" entries. @@ -15,7 +11,16 @@ provides it. * The User profile can now contain an "id" field if the auth-service provides it. -* New experimental feature: audit-logs (TODO) +* New experimental feature: audit-logs (TODO) - config "EnableAuditLogs" + + +2025-07-14 - v 0.9.4 +==================== + +=> Minimum Orthanc version: 1.11.3 <= +=> Recommended SDK version: 1.12.4 <= +=> Minimum SDK version: 1.11.3 <= + * Fixed a security issue: the entries in the cache token->permissions were kept too long in the cache allowing users to have access to generic routes even with an expired token. These entries are now stored maximum for 10 seconds. diff -r f1c8f36e0b87 -r b340ccd0b22a Plugin/Plugin.cpp --- a/Plugin/Plugin.cpp Mon Jul 14 11:40:39 2025 +0200 +++ b/Plugin/Plugin.cpp Mon Jul 14 18:23:36 2025 +0200 @@ -31,6 +31,7 @@ #include #include #include +#include "Enumerations.h" #define ORTHANC_PLUGIN_NAME "authorization" @@ -38,7 +39,7 @@ // Configuration of the authorization plugin static bool resourceTokensEnabled_ = false; static bool userTokensEnabled_ = false; -static bool enableAuditLogs_ = true; +static bool enableAuditLogs_ = false; static std::unique_ptr authorizationParser_; static std::unique_ptr authorizationService_; static std::unique_ptr permissionParser_; @@ -344,6 +345,43 @@ return false; } +static void RecordResourceAccessInternal(const OrthancPlugins::IAuthorizationService::UserProfile& profile, + const OrthancPlugins::IAuthorizationParser::AccessedResources& accesses, + const std::string& action, + const Json::Value& logData) +{ + for (OrthancPlugins::IAuthorizationParser::AccessedResources::const_iterator it = accesses.begin(); it != accesses.end(); ++it) + { + if (it->GetLevel() == OrthancPlugins::AccessLevel_Study) + { + RecordAuditLog(profile.userId, OrthancPluginResourceType_Study, it->GetOrthancId(), action, logData); + } + } +} + +static void RecordResourceAccess(const OrthancPlugins::IAuthorizationService::UserProfile& profile, + const std::string& uri, + OrthancPluginHttpMethod method, + const OrthancPlugins::AssociativeArray& getArguments) +{ + // Identify the resource + OrthancPlugins::IAuthorizationParser::AccessedResources accesses; + + if (authorizationParser_->Parse(accesses, uri, getArguments.GetMap())) + { + boost::smatch what; + + // Identify the action + boost::regex archive("^/(patients|studies|series|instances)/([a-f0-9-]+)/(archive|media)$"); + + if (boost::regex_match(uri, what, archive)) + { + RecordResourceAccessInternal(profile, accesses, "download", Json::nullValue); + } + } + +} + static bool TestRequiredPermissions(bool& hasUserRequiredPermissions, const std::set& requiredPermissions, const OrthancPlugins::IAuthorizationService::UserProfile& profile, @@ -367,6 +405,11 @@ if (hasAuthorizedLabelsForResource) { LOG(INFO) << msg2 << " -> granted"; + + if (enableAuditLogs_) + { + RecordResourceAccess(profile, uri, method, getArguments); + } } else { @@ -1185,6 +1228,14 @@ } } +void BulkModifyAnonymizeWithAuditLogs(OrthancPluginRestOutput* output, + const char* url, + const OrthancPluginHttpRequest* request) +{ + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "Auth plugin: Not implemented: Currently unable to perform bulk modification/anonymization with audit logs enabled."); +} + + void ModifyWithAuditLogs(OrthancPluginRestOutput* output, const char* url, const OrthancPluginHttpRequest* request) @@ -1939,6 +1990,9 @@ } #endif + enableAuditLogs_ = orthancFullConfiguration.GetBooleanValue("EnableAuditLogs", "false"); + + pluginConfiguration.LookupSetOfStrings(uncheckedResources_, "UncheckedResources", false); pluginConfiguration.LookupListOfStrings(uncheckedFolders_, "UncheckedFolders", false); @@ -2230,14 +2284,16 @@ OrthancPlugins::RegisterRestCallback("/(patients|studies|series)/([^/]*)/modify", true); OrthancPlugins::RegisterRestCallback("/(patients|studies|series)/([^/]*)/labels/([^/]*)", true); OrthancPlugins::RegisterRestCallback("/tools/bulk-delete", true); + OrthancPlugins::RegisterRestCallback("/tools/bulk-modify", true); + OrthancPlugins::RegisterRestCallback("/tools/bulk-anonymize", true); + + // Note: other "actions" that do not modify the data like download-archive are logged in the HTTP filter (see RecordResourceAccess()) // TODO - // OrthancPlugins::RegisterRestCallback("/tools/bulk-modify", true); // /modalities/move // /modalities/store // /archive + create-archive // /media + create-media + create-media-extended - // /bulk-anonymize + bulkd-modify } }