From 1bd153ea0b06d640c0d4424089b4e3fb1ed8f852 Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Fri, 27 Jun 2025 14:59:57 +0200 Subject: [PATCH] SYNC: audit log UX --- e2e-tests/cypress/e2e/team.cy.js | 15 ++++++++ packages/api/src/utility/auditlog.js | 55 +++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/e2e-tests/cypress/e2e/team.cy.js b/e2e-tests/cypress/e2e/team.cy.js index 5d757e96a..e1db8da23 100644 --- a/e2e-tests/cypress/e2e/team.cy.js +++ b/e2e-tests/cypress/e2e/team.cy.js @@ -80,4 +80,19 @@ describe('Team edition tests', () => { cy.testid('AdminMenuWidget_itemUsers').click(); cy.contains('test@example.com'); }); + + it.only('Audit logging', () => { + cy.testid('LoginPage_linkAdmin').click(); + cy.testid('LoginPage_password').type('adminpwd'); + cy.testid('LoginPage_submitLogin').click(); + + cy.testid('AdminMenuWidget_itemAuditLog').click(); + cy.contains('Audit log is not enabled'); + cy.testid('AdminMenuWidget_itemSettings').click(); + cy.testid('AdminSettingsTab_auditLogCheckbox').click(); + cy.testid('AdminMenuWidget_itemAuditLog').click(); + cy.contains('No data for selected date'); + + cy.themeshot('auditlog'); + }); }); diff --git a/packages/api/src/utility/auditlog.js b/packages/api/src/utility/auditlog.js index 6ef1001e7..3abd349ce 100644 --- a/packages/api/src/utility/auditlog.js +++ b/packages/api/src/utility/auditlog.js @@ -162,14 +162,31 @@ async function sendToAuditLog( } } -function maskPasswords(script) { +function maskLogFields(script) { return _.cloneDeepWith(script, (value, key) => { if (_.isString(key) && key.toLowerCase().includes('password')) { return '****'; } + if (key == 'query') { + return '****'; + } }); } +function extractConnectionId(connection) { + if ( + connection?.server == process.env.STORAGE_SERVER && + connection?.database == process.env.STORAGE_DATABASE && + connection?.port == process.env.STORAGE_PORT + ) { + return '__storage'; + } + if (connection?.conid) { + return connection.conid; + } + return null; +} + function analyseJsonRunnerScript(script) { const [assignSource, assignTarget, copyStream] = _.isArray(script?.commands) ? script?.commands : []; if (assignSource?.type != 'assign') { @@ -190,15 +207,15 @@ function analyseJsonRunnerScript(script) { return { category: 'import', component: 'RunnersController', - event: 'import.data', + event: 'import.table', action: 'import', severity: 'info', - message: 'Importing data', + message: `Importing table ${pureName}`, pureName: pureName, - conid: connection?.conid, + conid: extractConnectionId(connection), database: connection?.database, schemaName: schemaName, - detail: maskPasswords(script), + detail: maskLogFields(script), }; } return null; @@ -212,15 +229,33 @@ function analyseJsonRunnerScript(script) { return { category: 'export', component: 'RunnersController', - event: 'export.data', + event: 'export.table', action: 'export', severity: 'info', - message: 'Exporting data', + message: `Exporting table ${pureName}`, pureName: pureName, - conid: connection?.conid, + conid: extractConnectionId(connection), database: connection?.database, schemaName: schemaName, - detail: maskPasswords(script), + detail: maskLogFields(script), + }; + } + return null; + } + + if (assignSource?.functionName == 'queryReader') { + const connection = assignSource?.props?.connection; + if (connection) { + return { + category: 'export', + component: 'RunnersController', + event: 'export.query', + action: 'export', + severity: 'info', + message: 'Exporting query', + conid: extractConnectionId(connection), + database: connection?.database, + detail: maskLogFields(script), }; } return null; @@ -241,7 +276,7 @@ function logJsonRunnerScript(req, script) { event: 'script.run.json', action: 'script', severity: 'info', - detail: maskPasswords(script), + detail: maskLogFields(script), message: 'Running JSON script', }); }