[ACS-9736] Add deflating of audit values and intecepting them into audit record

This commit is contained in:
Kacper Magdziarz
2025-07-08 07:39:07 +02:00
parent e1067e3156
commit ecec9ddaaa
3 changed files with 73 additions and 27 deletions

View File

@@ -48,7 +48,6 @@ import org.alfresco.repo.audit.model.AuditModelRegistryImpl;
import org.alfresco.repo.domain.audit.AuditDAO; import org.alfresco.repo.domain.audit.AuditDAO;
import org.alfresco.repo.domain.propval.PropertyValueDAO; import org.alfresco.repo.domain.propval.PropertyValueDAO;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper;
@@ -73,8 +72,8 @@ public class AuditComponentImpl implements AuditComponent
{ {
private static final String INBOUND_LOGGER = "org.alfresco.repo.audit.inbound"; private static final String INBOUND_LOGGER = "org.alfresco.repo.audit.inbound";
private static Log logger = LogFactory.getLog(AuditComponentImpl.class); private static final Log logger = LogFactory.getLog(AuditComponentImpl.class);
private static Log loggerInbound = LogFactory.getLog(INBOUND_LOGGER); private static final Log loggerInbound = LogFactory.getLog(INBOUND_LOGGER);
private AuditModelRegistryImpl auditModelRegistry; private AuditModelRegistryImpl auditModelRegistry;
private PropertyValueDAO propertyValueDAO; private PropertyValueDAO propertyValueDAO;
@@ -221,7 +220,7 @@ public class AuditComponentImpl implements AuditComponent
public int deleteAuditEntries(List<Long> auditEntryIds) public int deleteAuditEntries(List<Long> auditEntryIds)
{ {
// Shortcut, if necessary // Shortcut, if necessary
if (auditEntryIds.size() == 0) if (auditEntryIds.isEmpty())
{ {
return 0; return 0;
} }
@@ -240,7 +239,7 @@ public class AuditComponentImpl implements AuditComponent
{ {
Long disabledPathsId = application.getDisabledPathsId(); Long disabledPathsId = application.getDisabledPathsId();
Set<String> disabledPaths = (Set<String>) propertyValueDAO.getPropertyById(disabledPathsId); Set<String> disabledPaths = (Set<String>) propertyValueDAO.getPropertyById(disabledPathsId);
return new HashSet<String>(disabledPaths); return new HashSet<>(disabledPaths);
} }
catch (Throwable e) catch (Throwable e)
{ {
@@ -315,7 +314,7 @@ public class AuditComponentImpl implements AuditComponent
{ {
PathMapper pathMapper = auditModelRegistry.getAuditPathMapper(); PathMapper pathMapper = auditModelRegistry.getAuditPathMapper();
Set<String> mappedPaths = pathMapper.getMappedPathsWithPartialMatch(path); Set<String> mappedPaths = pathMapper.getMappedPathsWithPartialMatch(path);
return loggerInbound.isDebugEnabled() || mappedPaths.size() > 0; return loggerInbound.isDebugEnabled() || !mappedPaths.isEmpty();
} }
/** /**
@@ -352,7 +351,7 @@ public class AuditComponentImpl implements AuditComponent
// Check if there are any entries that match or supercede the given path // Check if there are any entries that match or supercede the given path
String disablingPath = null; String disablingPath = null;
;
for (String disabledPath : disabledPaths) for (String disabledPath : disabledPaths)
{ {
if (path.startsWith(disabledPath)) if (path.startsWith(disabledPath))
@@ -579,7 +578,7 @@ public class AuditComponentImpl implements AuditComponent
} }
// Build the key paths using the session root path // Build the key paths using the session root path
Map<String, Serializable> pathedValues = new HashMap<String, Serializable>(values.size() * 2); Map<String, Serializable> pathedValues = new HashMap<>(values.size() * 2);
for (Map.Entry<String, Serializable> entry : values.entrySet()) for (Map.Entry<String, Serializable> entry : values.entrySet())
{ {
String pathElement = entry.getKey(); String pathElement = entry.getKey();
@@ -602,13 +601,10 @@ public class AuditComponentImpl implements AuditComponent
case TXN_NONE: case TXN_NONE:
case TXN_READ_ONLY: case TXN_READ_ONLY:
// New transaction // New transaction
RetryingTransactionCallback<Map<String, Serializable>> callback = new RetryingTransactionCallback<Map<String, Serializable>>() { RetryingTransactionCallback<Map<String, Serializable>> callback = () -> {
public Map<String, Serializable> execute() throws Throwable
{
Map<String, Serializable> recordedAuditValues = recordAuditValuesImpl(mappedValues); Map<String, Serializable> recordedAuditValues = recordAuditValuesImpl(mappedValues);
auditRecordReporter.reportAuditRecord(createAuditRecord(recordedAuditValues, true)); auditRecordReporter.reportAuditRecord(createAuditRecord(recordedAuditValues, true));
return recordedAuditValues; return recordedAuditValues;
}
}; };
RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
txnHelper.setForceWritable(true); txnHelper.setForceWritable(true);
@@ -628,7 +624,7 @@ public class AuditComponentImpl implements AuditComponent
public Map<String, Serializable> recordAuditValuesImpl(Map<String, Serializable> mappedValues) public Map<String, Serializable> recordAuditValuesImpl(Map<String, Serializable> mappedValues)
{ {
// Group the values by root path // Group the values by root path
Map<String, Map<String, Serializable>> mappedValuesByRootKey = new HashMap<String, Map<String, Serializable>>(); Map<String, Map<String, Serializable>> mappedValuesByRootKey = new HashMap<>();
for (Map.Entry<String, Serializable> entry : mappedValues.entrySet()) for (Map.Entry<String, Serializable> entry : mappedValues.entrySet())
{ {
String path = entry.getKey(); String path = entry.getKey();
@@ -636,7 +632,7 @@ public class AuditComponentImpl implements AuditComponent
Map<String, Serializable> rootKeyMappedValues = mappedValuesByRootKey.get(rootKey); Map<String, Serializable> rootKeyMappedValues = mappedValuesByRootKey.get(rootKey);
if (rootKeyMappedValues == null) if (rootKeyMappedValues == null)
{ {
rootKeyMappedValues = new HashMap<String, Serializable>(7); rootKeyMappedValues = new HashMap<>(7);
mappedValuesByRootKey.put(rootKey, rootKeyMappedValues); mappedValuesByRootKey.put(rootKey, rootKeyMappedValues);
} }
rootKeyMappedValues.put(path, entry.getValue()); rootKeyMappedValues.put(path, entry.getValue());
@@ -704,7 +700,7 @@ public class AuditComponentImpl implements AuditComponent
} }
// Check if there is anything to audit // Check if there is anything to audit
if (values.size() == 0) if (values.isEmpty())
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
@@ -737,12 +733,7 @@ public class AuditComponentImpl implements AuditComponent
Map<String, Serializable> auditData = generateData(generators); Map<String, Serializable> auditData = generateData(generators);
// Now extract values // Now extract values
Map<String, Serializable> extractedData = AuthenticationUtil.runAs(new RunAsWork<Map<String, Serializable>>() { Map<String, Serializable> extractedData = AuthenticationUtil.runAs(() -> extractData(application, values), AuthenticationUtil.getSystemUserName());
public Map<String, Serializable> doWork() throws Exception
{
return extractData(application, values);
}
}, AuthenticationUtil.getSystemUserName());
// Combine extracted and generated values (extracted data takes precedence) // Combine extracted and generated values (extracted data takes precedence)
auditData.putAll(extractedData); auditData.putAll(extractedData);
@@ -910,7 +901,7 @@ public class AuditComponentImpl implements AuditComponent
*/ */
private Map<String, Serializable> generateData(Map<String, DataGenerator> generators) private Map<String, Serializable> generateData(Map<String, DataGenerator> generators)
{ {
Map<String, Serializable> newData = new HashMap<String, Serializable>(generators.size() + 5); Map<String, Serializable> newData = new HashMap<>(generators.size() + 5);
for (Map.Entry<String, DataGenerator> entry : generators.entrySet()) for (Map.Entry<String, DataGenerator> entry : generators.entrySet())
{ {
String path = entry.getKey(); String path = entry.getKey();
@@ -937,8 +928,7 @@ public class AuditComponentImpl implements AuditComponent
private AuditRecord createAuditRecord(Map<String, ?> auditData, boolean inTransaction) private AuditRecord createAuditRecord(Map<String, ?> auditData, boolean inTransaction)
{ {
AuditRecord.Builder builder = new AuditRecord.Builder(); AuditRecord.Builder builder = AuditRecordUtils.generateAuditRecordBuilder(auditData);
builder.setAuditData(auditData);
builder.setInTransaction(inTransaction); builder.setInTransaction(inTransaction);
return builder.build(); return builder.build();
} }

View File

@@ -31,11 +31,13 @@ import java.util.Map;
public class AuditRecord public class AuditRecord
{ {
private final boolean inTransaction; private final boolean inTransaction;
private final String auditApplicationId;
private final ZonedDateTime createdAt; private final ZonedDateTime createdAt;
private final Map<String, ?> auditData; private final Map<String, ?> auditData;
public AuditRecord(Builder builder) public AuditRecord(Builder builder)
{ {
this.auditApplicationId = builder.auditApplicationId;
this.inTransaction = builder.inTransaction; this.inTransaction = builder.inTransaction;
this.auditData = builder.auditData; this.auditData = builder.auditData;
this.createdAt = ZonedDateTime.now(); this.createdAt = ZonedDateTime.now();
@@ -46,6 +48,11 @@ public class AuditRecord
return inTransaction; return inTransaction;
} }
public String getAuditApplicationId()
{
return auditApplicationId;
}
public ZonedDateTime getCreatedAt() public ZonedDateTime getCreatedAt()
{ {
return createdAt; return createdAt;
@@ -63,9 +70,16 @@ public class AuditRecord
public static class Builder public static class Builder
{ {
private String auditApplicationId;
private boolean inTransaction; private boolean inTransaction;
private Map<String, ?> auditData; private Map<String, ?> auditData;
public Builder setAuditApplicationId(String auditApplicationId)
{
this.auditApplicationId = auditApplicationId;
return this;
}
public Builder setInTransaction(boolean inTransaction) public Builder setInTransaction(boolean inTransaction)
{ {
this.inTransaction = inTransaction; this.inTransaction = inTransaction;

View File

@@ -0,0 +1,42 @@
package org.alfresco.repo.audit;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class AuditRecordUtils
{
/**
* This method will generate {@link AuditRecord#builder()} from provided structured audit data. Provided data will be translated from `key - value` structure to json structure. Generated builder will be preloaded with {@link AuditRecord#auditApplicationId} and {@link AuditRecord#auditData}
*
* @param data
* represent `key - value` structured map that contains audit data.
* @return preloaded {@link AuditRecord#builder()}.
*/
@SuppressWarnings("unchecked")
public static AuditRecord.Builder generateAuditRecordBuilder(Map<String, ?> data)
{
var auditRecordBuilder = AuditRecord.builder();
var rootNode = new HashMap<String, Serializable>();
data.forEach((k, v) -> {
var keys = k.split("/");
auditRecordBuilder.setAuditApplicationId(keys[0]);
var current = rootNode;
for (int i = 1; i < keys.length - 1; i++)
{
if (!current.containsKey(keys[i]) || !(current.get(keys[i]) instanceof ObjectNode))
{
current.put(keys[i], new HashMap<String, Serializable>());
}
current = (HashMap<String, Serializable>) current.get(keys[i]);
}
current.put(keys[keys.length - 1], v.toString());
});
auditRecordBuilder.setAuditData(rootNode);
return auditRecordBuilder;
}
}