changeset 715:7c0157bf749c sql-opti

added the name of the source plugin in audit logs
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sun, 10 Aug 2025 15:27:45 +0200
parents c1b73bf5c122
children 951d2ef62f58
files Framework/Plugins/DatabaseBackendAdapterV4.cpp Framework/Plugins/IDatabaseBackend.h Framework/Plugins/IndexBackend.cpp Framework/Plugins/IndexBackend.h MySQL/Plugins/IndexPlugin.cpp Odbc/Plugins/IndexPlugin.cpp PostgreSQL/Plugins/SQL/PrepareIndex.sql SQLite/Plugins/IndexPlugin.cpp
diffstat 8 files changed, 125 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Plugins/DatabaseBackendAdapterV4.cpp	Fri Aug 08 18:01:19 2025 +0200
+++ b/Framework/Plugins/DatabaseBackendAdapterV4.cpp	Sun Aug 10 15:27:45 2025 +0200
@@ -1525,7 +1525,8 @@
   }
 
 
-  OrthancPluginErrorCode AuditLogHandler(const char*               userId,
+  OrthancPluginErrorCode AuditLogHandler(const char*               sourcePlugin,
+                                         const char*               userId,
                                          OrthancPluginResourceType resourceType,
                                          const char*               resourceId,
                                          const char*               action,
@@ -1542,6 +1543,7 @@
     {
       BaseIndexConnectionsPool::Accessor accessor(*connectionPool_);
       accessor.GetBackend().RecordAuditLog(accessor.GetManager(),
+                                           sourcePlugin,
                                            userId,
                                            resourceType,
                                            resourceId,
@@ -1612,7 +1614,6 @@
       toTs = boost::lexical_cast<uint64_t>(getArguments["to-timestamp"]);
     }
 
-    // note: right now, we assume the logData is always JSON
     Json::Value jsonLogs;
 
 #if ORTHANC_PLUGINS_HAS_AUDIT_LOGS == 1
@@ -1631,20 +1632,51 @@
       for (std::list<IDatabaseBackend::AuditLog>::const_iterator it = logs.begin(); it != logs.end(); ++it)
       {
         Json::Value serializedAuditLog;
-        serializedAuditLog["Timestamp"] = it->timeStamp;
-        serializedAuditLog["UserId"] = it->userId;
-        serializedAuditLog["ResourceId"] = it->resourceId;
-        serializedAuditLog["ResourceType"] = it->resourceType;
-        serializedAuditLog["Action"] = it->action;
+        serializedAuditLog["SourcePlugin"] = it->GetSourcePlugin();
+        serializedAuditLog["Timestamp"] = it->GetTimestamp();
+        serializedAuditLog["UserId"] = it->GetUserId();
+        serializedAuditLog["ResourceId"] = it->GetResourceId();
+        serializedAuditLog["Action"] = it->GetAction();
+
+        std::string level;
+        switch (it->GetResourceType())
+        {
+          case OrthancPluginResourceType_Patient:
+            level = "Patient";
+            break;
+
+          case OrthancPluginResourceType_Study:
+            level = "Study";
+            break;
 
-        if (it->logData.empty())
+          case OrthancPluginResourceType_Series:
+            level = "Series";
+            break;
+
+          case OrthancPluginResourceType_Instance:
+            level = "Instance";
+            break;
+
+          case OrthancPluginResourceType_None:
+            level = "None";
+            break;
+
+          default:
+            throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        serializedAuditLog["ResourceType"] = level;
+
+        // TODO - Shouldn't the "LogData" information be Base64-encoded?
+        // Plugins are not required to write JSON (e.g., could be Protocol Buffers)
+        if (it->GetLogData().empty())
         {
           serializedAuditLog["LogData"] = Json::nullValue;
         }
         else
         {
           Json::Value logData;
-          Orthanc::Toolbox::ReadJson(logData, it->logData);
+          Orthanc::Toolbox::ReadJson(logData, it->GetLogData());
           serializedAuditLog["LogData"] = logData;
         }
 
--- a/Framework/Plugins/IDatabaseBackend.h	Fri Aug 08 18:01:19 2025 +0200
+++ b/Framework/Plugins/IDatabaseBackend.h	Sun Aug 10 15:27:45 2025 +0200
@@ -37,28 +37,68 @@
   class IDatabaseBackend : public boost::noncopyable
   {
   public:
-    struct AuditLog
+    class AuditLog
     {
-      std::string timeStamp;
-      std::string userId;
-      OrthancPluginResourceType resourceType;
-      std::string resourceId;
-      std::string action;
-      std::string logData;
+    private:
+      std::string timestamp_;
+      std::string sourcePlugin_;
+      std::string userId_;
+      OrthancPluginResourceType resourceType_;
+      std::string resourceId_;
+      std::string action_;
+      std::string logData_;
 
-      AuditLog(const std::string& timeStamp,
+    public:
+      AuditLog(const std::string& timestamp,
+               const std::string& sourcePlugin,
                const std::string& userId,
                OrthancPluginResourceType resourceType,
                const std::string& resourceId,
                const std::string& action,
                const std::string& logData) :
-        timeStamp(timeStamp),
-        userId(userId),
-        resourceType(resourceType),
-        resourceId(resourceId),
-        action(action),
-        logData(logData)
+        timestamp_(timestamp),
+        sourcePlugin_(sourcePlugin),
+        userId_(userId),
+        resourceType_(resourceType),
+        resourceId_(resourceId),
+        action_(action),
+        logData_(logData)
+      {
+      }
+
+      const std::string& GetTimestamp() const
+      {
+        return timestamp_;
+      }
+
+      const std::string& GetSourcePlugin() const
+      {
+        return sourcePlugin_;
+      }
+
+      const std::string& GetUserId() const
       {
+        return userId_;
+      }
+
+      OrthancPluginResourceType GetResourceType() const
+      {
+        return resourceType_;
+      }
+
+      const std::string& GetResourceId() const
+      {
+        return resourceId_;
+      }
+
+      const std::string& GetAction() const
+      {
+        return action_;
+      }
+
+      const std::string& GetLogData() const
+      {
+        return logData_;
       }
     };
 
@@ -489,6 +529,7 @@
 
 #if ORTHANC_PLUGINS_HAS_AUDIT_LOGS == 1
     virtual void RecordAuditLog(DatabaseManager& manager,
+                                const std::string& sourcePlugin,
                                 const std::string& userId,
                                 OrthancPluginResourceType type,
                                 const std::string& resourceId,
--- a/Framework/Plugins/IndexBackend.cpp	Fri Aug 08 18:01:19 2025 +0200
+++ b/Framework/Plugins/IndexBackend.cpp	Sun Aug 10 15:27:45 2025 +0200
@@ -4685,6 +4685,7 @@
 
 #if ORTHANC_PLUGINS_HAS_AUDIT_LOGS == 1
     void IndexBackend::RecordAuditLog(DatabaseManager& manager,
+                                      const std::string& sourcePlugin,
                                       const std::string& userId,
                                       OrthancPluginResourceType resourceType,
                                       const std::string& resourceId,
@@ -4694,22 +4695,22 @@
     {
       DatabaseManager::CachedStatement statement(
         STATEMENT_FROM_HERE, manager,
-        "INSERT INTO AuditLogs (userId, resourceType, resourceId, action, logData) "
-        "VALUES(${userId}, ${resourceType}, ${resourceId}, ${action}, ${logData})");
-
+        "INSERT INTO AuditLogs (sourcePlugin, userId, resourceType, resourceId, action, logData) "
+        "VALUES(${sourcePlugin}, ${userId}, ${resourceType}, ${resourceId}, ${action}, ${logData})");
+
+      statement.SetParameterType("sourcePlugin", ValueType_Utf8String);
       statement.SetParameterType("userId", ValueType_Utf8String);
+      statement.SetParameterType("resourceType", ValueType_Integer64);
       statement.SetParameterType("resourceId", ValueType_Utf8String);
-      statement.SetParameterType("resourceType", ValueType_Integer64);
       statement.SetParameterType("action", ValueType_Utf8String);
-      statement.SetParameterType("userId", ValueType_Utf8String);
       statement.SetParameterType("logData", ValueType_BinaryString);
 
       Dictionary args;
+      args.SetUtf8Value("sourcePlugin", sourcePlugin);
       args.SetUtf8Value("userId", userId);
       args.SetIntegerValue("resourceType", static_cast<int>(resourceType));
       args.SetUtf8Value("resourceId", resourceId);
       args.SetUtf8Value("action", action);
-      args.SetUtf8Value("userId", userId);
       
       if (logData != NULL && logDataSize > 0)
       {
@@ -4736,7 +4737,7 @@
       LookupFormatter formatter(manager.GetDialect());
       std::vector<std::string> filters;
 
-      std::string sql = "SELECT to_char(ts, 'YYYY-MM-DD\"T\"HH24:MI:SS.MS\"Z\"'), userId, resourceType, resourceId, action, logData FROM AuditLogs ";
+      std::string sql = "SELECT to_char(ts, 'YYYY-MM-DD\"T\"HH24:MI:SS.MS\"Z\"'), sourcePlugin, userId, resourceType, resourceId, action, logData FROM AuditLogs ";
 
       if (!userIdFilter.empty())
       {
@@ -4791,19 +4792,21 @@
         
         statement.SetResultFieldType(0, ValueType_Utf8String);
         statement.SetResultFieldType(1, ValueType_Utf8String);
-        statement.SetResultFieldType(2, ValueType_Integer64);
-        statement.SetResultFieldType(3, ValueType_Utf8String);
+        statement.SetResultFieldType(2, ValueType_Utf8String);
+        statement.SetResultFieldType(3, ValueType_Integer64);
         statement.SetResultFieldType(4, ValueType_Utf8String);
-        statement.SetResultFieldType(5, ValueType_BinaryString);
+        statement.SetResultFieldType(5, ValueType_Utf8String);
+        statement.SetResultFieldType(6, ValueType_BinaryString);
 
         while (!statement.IsDone())
         {
           logs.push_back(AuditLog(statement.ReadString(0),
                                   statement.ReadString(1),
-                                  static_cast<OrthancPluginResourceType>(statement.ReadInteger64(2)),
-                                  statement.ReadString(3),
+                                  statement.ReadString(2),
+                                  static_cast<OrthancPluginResourceType>(statement.ReadInteger64(3)),
                                   statement.ReadString(4),
-                                  statement.ReadStringOrNull(5)));
+                                  statement.ReadString(5),
+                                  statement.ReadStringOrNull(6)));
 
           statement.Next();
         }
--- a/Framework/Plugins/IndexBackend.h	Fri Aug 08 18:01:19 2025 +0200
+++ b/Framework/Plugins/IndexBackend.h	Sun Aug 10 15:27:45 2025 +0200
@@ -523,6 +523,7 @@
 
 #if ORTHANC_PLUGINS_HAS_AUDIT_LOGS == 1
     virtual void RecordAuditLog(DatabaseManager& manager,
+                                const std::string& sourcePlugin,
                                 const std::string& userId,
                                 OrthancPluginResourceType type,
                                 const std::string& resourceId,
@@ -539,10 +540,8 @@
                               uint64_t toTs,
                               uint64_t since,
                               uint64_t limit) ORTHANC_OVERRIDE;
-
 #endif
 
-
     virtual bool HasPerformDbHousekeeping() ORTHANC_OVERRIDE
     {
       return false;
--- a/MySQL/Plugins/IndexPlugin.cpp	Fri Aug 08 18:01:19 2025 +0200
+++ b/MySQL/Plugins/IndexPlugin.cpp	Sun Aug 10 15:27:45 2025 +0200
@@ -86,8 +86,11 @@
 
       OrthancDatabases::MySQLParameters parameters(mysql, configuration);
       OrthancDatabases::IndexBackend::Register(
-        new OrthancDatabases::MySQLIndex(context, parameters, readOnly), countConnections,
-        parameters.GetMaxConnectionRetries(), housekeepingDelaySeconds);
+        new OrthancDatabases::MySQLIndex(context, parameters, readOnly),
+        countConnections,
+        false /* useDynamicConnectionPool */,
+        parameters.GetMaxConnectionRetries(),
+        housekeepingDelaySeconds);
     }
     catch (Orthanc::OrthancException& e)
     {
--- a/Odbc/Plugins/IndexPlugin.cpp	Fri Aug 08 18:01:19 2025 +0200
+++ b/Odbc/Plugins/IndexPlugin.cpp	Sun Aug 10 15:27:45 2025 +0200
@@ -130,7 +130,11 @@
       index->SetMaxConnectionRetries(maxConnectionRetries);
       index->SetConnectionRetryInterval(connectionRetryInterval);
 
-      OrthancDatabases::IndexBackend::Register(index.release(), countConnections, maxConnectionRetries, housekeepingDelaySeconds);
+      OrthancDatabases::IndexBackend::Register(index.release(),
+                                               countConnections,
+                                               false /* useDynamicConnectionPool */,
+                                               maxConnectionRetries,
+                                               housekeepingDelaySeconds);
     }
     catch (Orthanc::OrthancException& e)
     {
--- a/PostgreSQL/Plugins/SQL/PrepareIndex.sql	Fri Aug 08 18:01:19 2025 +0200
+++ b/PostgreSQL/Plugins/SQL/PrepareIndex.sql	Sun Aug 10 15:27:45 2025 +0200
@@ -826,6 +826,7 @@
 
 CREATE TABLE IF NOT EXISTS AuditLogs (
     ts TIMESTAMP DEFAULT NOW(),
+    sourcePlugin TEXT NOT NULL,
     userId TEXT NOT NULL,
     resourceType INTEGER NOT NULL,
     resourceId VARCHAR(64) NOT NULL,
--- a/SQLite/Plugins/IndexPlugin.cpp	Fri Aug 08 18:01:19 2025 +0200
+++ b/SQLite/Plugins/IndexPlugin.cpp	Sun Aug 10 15:27:45 2025 +0200
@@ -75,6 +75,7 @@
       OrthancDatabases::IndexBackend::Register(
         new OrthancDatabases::SQLiteIndex(context, "index.db"),   // TODO parameter
         1 /* only 1 connection is possible with SQLite */,
+        false /* useDynamicConnectionPool */,
         0 /* no collision is possible, as SQLite has a global lock */,
         0 /* housekeeping delay, unused for SQLite */);
     }