Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
af3a89f
Add file system audit events for files added to domains and record or…
labkey-susanh Jul 10, 2025
e66e4d4
Conditional creation of audit event in savePostedFiles
labkey-susanh Jul 10, 2025
6fbc40f
General cleanup and add log in proper place when tempdirectory is in use
labkey-susanh Jul 11, 2025
d58b0a3
Minor code tidying
labkey-susanh Jul 14, 2025
db581b2
@labkey/components v6.56.1-fileNameAudit.1 and add timeline event det…
labkey-susanh Jul 14, 2025
082094f
For async assay import, set the transaction id in the context so all …
labkey-susanh Jul 14, 2025
dd2b8e9
Merge remote-tracking branch 'origin/develop' into fb_fileNameAudit
labkey-susanh Jul 14, 2025
639879b
Add audit event for run file uploading
labkey-susanh Jul 15, 2025
3ccfaa3
Merge remote-tracking branch 'origin/develop' into fb_fileNameAudit
labkey-susanh Jul 15, 2025
aa64729
Merge remote-tracking branch 'origin/develop' into fb_fileNameAudit
labkey-susanh Jul 22, 2025
adea0f9
Set transaction id in audit event constructors
labkey-susanh Jul 22, 2025
e7db28e
Merge remote-tracking branch 'origin/develop' into fb_fileNameAudit
labkey-susanh Jul 23, 2025
6a4c5d7
Comment
labkey-susanh Jul 23, 2025
8590108
Remove relabeling of "Created" as "End Time" (since it's not really a…
labkey-susanh Jul 23, 2025
5289a83
Use non-default constructor
labkey-susanh Jul 24, 2025
b5c536c
Merge remote-tracking branch 'origin/develop' into fb_fileNameAudit
labkey-susanh Jul 24, 2025
33384bd
Merge remote-tracking branch 'origin/develop' into fb_fileNameAudit
labkey-susanh Jul 25, 2025
75e122f
Merge remote-tracking branch 'origin/develop' into fb_fileNameAudit
labkey-susanh Jul 28, 2025
4f80968
Fix directory
labkey-susanh Jul 28, 2025
7b44453
Use target file in event not source file
labkey-susanh Jul 28, 2025
aac3452
Move common code
labkey-susanh Jul 28, 2025
efd74bb
Add tests for data set file field auditing
labkey-susanh Jul 28, 2025
650f29d
Update locator for random field name
labkey-susanh Jul 29, 2025
d34ac95
Adjust container for move audit events and add test for assay run moves
labkey-susanh Jul 29, 2025
c49ff8b
Merge remote-tracking branch 'origin/develop' into fb_fileNameAudit
labkey-susanh Jul 29, 2025
e4b1023
Merge remote-tracking branch 'origin/develop' into fb_fileNameAudit
labkey-susanh Jul 30, 2025
b3b373c
Update test with domain kind and new field names
labkey-susanh Jul 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions api/src/org/labkey/api/ApiModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.labkey.api.action.ApiXmlWriter;
import org.labkey.api.action.SpringActionController;
import org.labkey.api.admin.SubfolderWriter;
import org.labkey.api.assay.AssayFileWriter;
import org.labkey.api.assay.AssayResultsFileWriter;
import org.labkey.api.assay.ReplacedRunFilter;
import org.labkey.api.assay.sample.MaterialInputRoleComparator;
Expand Down Expand Up @@ -445,7 +444,6 @@ public void registerServlets(ServletContext servletCtx)
Table.IsSelectTestCase.class,
ValidEmail.TestCase.class,
URIUtil.TestCase.class,
AssayFileWriter.TestCase.class,
AssayResultsFileWriter.TestCase.class
);
}
Expand Down
4 changes: 2 additions & 2 deletions api/src/org/labkey/api/action/AbstractFileUploadAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package org.labkey.api.action;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.labkey.api.util.ExceptionUtil;
import org.labkey.api.util.FileUtil;
import org.labkey.api.util.PageFlowUtil;
Expand All @@ -28,8 +30,6 @@
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
Expand Down
38 changes: 30 additions & 8 deletions api/src/org/labkey/api/assay/AbstractAssayProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.labkey.api.assay.transform.DataExchangeHandler;
import org.labkey.api.assay.transform.DataTransformService;
import org.labkey.api.audit.AuditLogService;
import org.labkey.api.audit.provider.FileSystemAuditProvider;
import org.labkey.api.data.ActionButton;
import org.labkey.api.data.ButtonBar;
import org.labkey.api.data.ColumnInfo;
Expand Down Expand Up @@ -136,7 +137,6 @@
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
Expand Down Expand Up @@ -1798,7 +1798,7 @@ public record AssayFileMoveData(ExpRun run, Container sourceContainer, String fi
public record AssayMoveData(Map<String, Integer> counts, Map<Integer, List<AssayFileMoveData>> fileMovesByRunId) {}

@Override
public void moveRuns(List<ExpRun> runs, Container targetContainer, User user, AbstractAssayProvider.AssayMoveData assayMoveData) throws ExperimentException
public void moveRuns(List<ExpRun> runs, Container targetContainer, User user, AssayMoveData assayMoveData) throws ExperimentException
{
if (runs.isEmpty())
return;
Expand Down Expand Up @@ -1968,7 +1968,7 @@ private void moveRunsBatch(List<ExpRun> runs, Container sourceContainer, Contain
if (updatedFile != null)
{
if (!fileMoveReferences.containsKey(sourceFileName))
fileMoveReferences.put(sourceFileName, new AssayFileMoveReference(sourceFileName, updatedFile, experiment.getName()));
fileMoveReferences.put(sourceFileName, new AssayFileMoveReference(sourceFileName, updatedFile, experiment.getName(), fileProp.getName()));

if (!fileMoveCounts.containsKey(sourceFileName))
fileMoveCounts.put(sourceFileName, 0);
Expand All @@ -1991,6 +1991,11 @@ private void moveRunsBatch(List<ExpRun> runs, Container sourceContainer, Contain
throw new ExperimentException("Assay batch " + ref.runName + " cannot be moved since it references a shared file: " + sourceFile.getName());

fileContentService.fireFileMoveEvent(sourceFile.toPath(), ref.updatedFile.toPath(), user, sourceContainer, targetContainer);
FileSystemAuditProvider.FileSystemAuditEvent event = new FileSystemAuditProvider.FileSystemAuditEvent(targetContainer, "File moved from " + sourceContainer.getPath() + " to " + targetContainer.getPath() + ".");
event.setProvidedFileName(sourceFile.getName());
event.setFile(ref.updatedFile.getName());
event.setDirectory(ref.updatedFile.getParent());
AuditLogService.get().addEvent(user, event);
}

}
Expand Down Expand Up @@ -2050,7 +2055,7 @@ private void updateRunFiles(List<ExpRun> runs, Container sourceContainer, Contai
if (updatedFile != null)
{
if (!fileMoveReferences.containsKey(sourceFileName))
fileMoveReferences.put(sourceFileName, new AssayFileMoveReference(sourceFileName, updatedFile, run.getName()));
fileMoveReferences.put(sourceFileName, new AssayFileMoveReference(sourceFileName, updatedFile, run.getName(), fileProp.getName()));

if (!fileMoveCounts.containsKey(sourceFileName))
fileMoveCounts.put(sourceFileName, 0);
Expand All @@ -2072,6 +2077,12 @@ private void updateRunFiles(List<ExpRun> runs, Container sourceContainer, Contai
throw new ExperimentException("Assay run " + ref.runName + " cannot be moved since it references a shared file: " + sourceFile.getName());

fileContentService.fireFileMoveEvent(sourceFile.toPath(), ref.updatedFile.toPath(), user, sourceContainer, targetContainer);
FileSystemAuditProvider.FileSystemAuditEvent event = new FileSystemAuditProvider.FileSystemAuditEvent(targetContainer, "File moved from " + sourceContainer.getPath() + " to " + targetContainer.getPath() + ".");
event.setProvidedFileName(sourceFile.getName());
event.setFile(ref.updatedFile.getName());
event.setDirectory(ref.updatedFile.getParent());
event.setFieldName(ref.fieldName);
AuditLogService.get().addEvent(user, event);
}

}
Expand Down Expand Up @@ -2116,6 +2127,11 @@ private void updateDataFileUrl(List<ExpRun> runs, Container sourceContainer, Con
movedFiles.putIfAbsent(runId, new ArrayList<>());
movedFiles.get(runId).add(new AssayFileMoveData(run, run.getContainer(), null, sourceFile, updatedFile));
fileContentService.fireFileMoveEvent(sourceFile.toPath(), updatedFile.toPath(), user, sourceContainer, targetContainer);
FileSystemAuditProvider.FileSystemAuditEvent event = new FileSystemAuditProvider.FileSystemAuditEvent(targetContainer, "File moved from " + sourceContainer.getPath() + " to " + targetContainer.getPath() + ".");
event.setProvidedFileName(sourceFile.getName());
event.setFile(updatedFile.getName());
event.setDirectory(updatedFile.getParent());
AuditLogService.get().addEvent(user, event);
}
}
catch (Exception e)
Expand All @@ -2136,7 +2152,7 @@ protected void moveAssayResults(List<ExpRun> runs, ExpProtocol protocol, Contain
updateResultFiles(assayResultTable, runs, protocol, sourceContainer, targetContainer, user, assayMoveData);
}

record AssayFileMoveReference(String sourceFilePath, File updatedFile, String runName) {}
record AssayFileMoveReference(String sourceFilePath, File updatedFile, String runName, String fieldName) {}

private void updateResultFiles(FilteredTable assayResultTable, List<ExpRun> runs, ExpProtocol assayProtocol, Container sourceContainer, Container targetContainer, User user, AssayMoveData assayMoveData) throws ExperimentException
{
Expand Down Expand Up @@ -2169,10 +2185,10 @@ private void updateResultFiles(FilteredTable assayResultTable, List<ExpRun> runs
Map<Integer, ExpRun> runMap = new HashMap<>();
runs.forEach(run -> runMap.put(run.getRowId(), run));

Map<String, AssayFileMoveReference> fileMoveReferences = new HashMap<>();
Map<String, List<Integer>> fileMoveResultRowIds = new HashMap<>();
for (String fileField : fileFields)
{
Map<String, AssayFileMoveReference> fileMoveReferences = new HashMap<>();
Map<String, List<Integer>> fileMoveResultRowIds = new HashMap<>();
var fileColumn = assayResultTable.getColumn(fileField);
TableSelector ts = new TableSelector(assayResultTable, assayResultTable.getColumns("rowid", "run", fileField), filter, null);
Map<String, Object>[] resultFiles = ts.getMapArray();
Expand All @@ -2194,7 +2210,7 @@ private void updateResultFiles(FilteredTable assayResultTable, List<ExpRun> runs
ExpRun run = runMap.get(resultRunId);

if (!fileMoveReferences.containsKey(sourceFileName))
fileMoveReferences.put(sourceFileName, new AssayFileMoveReference(sourceFileName, updatedFile, run.getName()));
fileMoveReferences.put(sourceFileName, new AssayFileMoveReference(sourceFileName, updatedFile, run.getName(),fileField));

movedFiles.putIfAbsent(resultRunId, new ArrayList<>());
movedFiles.get(resultRunId).add(new AssayFileMoveData(run, run.getContainer(), fileField, sourceFile, updatedFile));
Expand Down Expand Up @@ -2234,6 +2250,12 @@ private void updateResultFiles(FilteredTable assayResultTable, List<ExpRun> runs
.append(" WHERE rowId ").appendInClause(fileMoveResultRowIds.get(sourceFileName), realTable.getSqlDialect());
new SqlExecutor(assayResultTable.getSchema()).execute(updateSql);

FileSystemAuditProvider.FileSystemAuditEvent event = new FileSystemAuditProvider.FileSystemAuditEvent(targetContainer, "File moved from " + sourceContainer.getPath() + " to " + targetContainer.getPath() + ".");
event.setProvidedFileName(sourceFile.getName());
event.setFile(updatedFile.getName());
event.setDirectory(updatedFile.getParent());
event.setFieldName(ref.fieldName);
AuditLogService.get().addEvent(user, event);
}

}
Expand Down
40 changes: 14 additions & 26 deletions api/src/org/labkey/api/assay/AbstractTempDirDataCollector.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,18 @@
package org.labkey.api.assay;

import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import org.labkey.api.assay.actions.AssayRunUploadForm;
import org.labkey.api.audit.AuditLogService;
import org.labkey.api.audit.provider.FileSystemAuditProvider;
import org.labkey.api.exp.ExperimentException;
import org.labkey.api.exp.api.ExpData;
import org.labkey.api.exp.api.ExpRun;
import org.labkey.api.query.BatchValidationException;
import org.labkey.api.util.FileUtil;
import org.labkey.api.util.NetworkDrive;
import org.labkey.api.util.logging.LogHelper;
import org.labkey.vfs.FileLike;

import java.io.File;
Expand All @@ -43,17 +45,16 @@
public abstract class AbstractTempDirDataCollector<ContextType extends AssayRunUploadContext<? extends AssayProvider>> extends AbstractAssayDataCollector<ContextType>
{
protected boolean _uploadComplete = false;
private static final String TMPFILE = "tmp";

private static final Logger LOG = LogManager.getLogger(AbstractTempDirDataCollector.class);
private static final Logger LOG = LogHelper.getLogger(AbstractTempDirDataCollector.class, "Activity related to data collection into a temporary directory");

private void removeTempDir(ContextType context) throws ExperimentException
{
// TODO: remove this instanceof check
if (!(context instanceof AssayRunUploadForm))
return;

String uploadAttemptID = ((AssayRunUploadForm)context).getUploadAttemptID();
String uploadAttemptID = ((AssayRunUploadForm<?>) context).getUploadAttemptID();

// Cleanup files other than input generated by transform scripts
FileLike tempDir = ensureSubdirectory(context.getContainer(), TEMP_DIR_NAME);
Expand All @@ -71,24 +72,6 @@ private void removeTempDir(ContextType context) throws ExperimentException
}
}

private void removeFiles(ContextType context, Collection<Map<String, File>> assayFiles) throws ExperimentException
{
for (Map<String, File> fileMap : assayFiles)
{
for (File file : fileMap.values())
{
FileLike assayFile = ensureUploadDirectory(context.getContainer(), DIR_NAME).resolveChild(file.getName());
FileUtils.deleteQuietly(assayFile.toNioPathForWrite().toFile());
}
}
}

protected void uploadFailed(ContextType context, List<Map<String, File>> assayFiles) throws ExperimentException
{
removeTempDir(context);
removeFiles(context, assayFiles);
}

@Override
public void initDir(ContextType context) throws ExperimentException
{
Expand All @@ -99,7 +82,7 @@ public void initDir(ContextType context) throws ExperimentException
if (!(context instanceof AssayRunUploadForm))
return;

String uploadAttemptID = ((AssayRunUploadForm)context).getUploadAttemptID();
String uploadAttemptID = ((AssayRunUploadForm<?>)context).getUploadAttemptID();

// Cleanup transform script output files if generated by warning feature
FileLike tempDir = AssayFileWriter.ensureSubdirectory(context.getContainer(), TEMP_DIR_NAME);
Expand Down Expand Up @@ -140,7 +123,7 @@ protected FileLike getFileTargetDir(ContextType context) throws ExperimentExcept
try
{
FileLike tempDir = ensureSubdirectory(context.getContainer(), TEMP_DIR_NAME);
uploadAttemptDir = tempDir.resolveChild(((AssayRunUploadForm)context).getUploadAttemptID());
uploadAttemptDir = tempDir.resolveChild(((AssayRunUploadForm<?>)context).getUploadAttemptID());

if (!NetworkDrive.exists(uploadAttemptDir))
{
Expand Down Expand Up @@ -169,7 +152,7 @@ protected FileLike getFileTargetDir(ContextType context) throws ExperimentExcept
protected FileLike getFilePath(ContextType context, @Nullable ExpRun run, FileLike tempDirFile) throws ExperimentException
{
FileLike assayDir = ensureUploadDirectory(context.getContainer());
return findUniqueFileName(tempDirFile.getName(), assayDir);
return FileUtil.findUniqueFileName(tempDirFile.getName(), assayDir);
}

// This is the default case to move the primary file from the temp directory to the assayData directory
Expand Down Expand Up @@ -229,10 +212,15 @@ public Map<String, FileLike> uploadComplete(ContextType context, @Nullable ExpRu
run.save(context.getUser());
}
handleTempFile(tempDirFile.toNioPathForWrite().toFile(), assayDirFile.toNioPathForWrite().toFile());
FileSystemAuditProvider.FileSystemAuditEvent event = new FileSystemAuditProvider.FileSystemAuditEvent(context.getContainer(), "Primary file provided for assay import");
event.setProvidedFileName(tempDirFile.getName());
event.setFile(assayDirFile.getName());
event.setDirectory(assayDirFile.getParent().toURI().getPath());
AuditLogService.get().addEvent(context.getUser(), event);
}
else
{
LOG.warn("Unable to resolve import/upload file location for file: " + tempDirFile);
LOG.warn("Unable to resolve import/upload file location for file: {}", tempDirFile);
}
}
FileUtils.deleteDirectory(tempDir.toNioPathForWrite().toFile());
Expand Down
Loading