Skip to content

File path related issue bundle #6847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 36 commits into from
Aug 5, 2025
Merged

File path related issue bundle #6847

merged 36 commits into from
Aug 5, 2025

Conversation

XingY
Copy link
Contributor

@XingY XingY commented Jul 15, 2025

Rationale

This PR include the following changes:

  • Reject out-of-scope (path outside of file root) file paths and path that doesn't exist on insert/update/import or update via file.
  • Make sure display/messaging doesn’t reveal file existence information by using a generic error message for invalid file on import/update, and generic handling of rendering unavailable files without revealing the reason.
  • Only show the file name and not file full path. This include disabling filter/sort for file columns and stop including full path in api responses and custom property views, etc.

Related Pull Requests

Changes

{
URI uri = FileUtil.createUri(filePath);
if (FileUtil.FILE_SCHEME.equals(uri.getScheme()))
return new File(uri);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@labkey-matthewb Do you have any suggestions how to get around using new File and maybe use FileLike here? In order for f.isAbsolute() to work for path that starts with file:/, I had to convert string to uri first before passing to File constructor.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are stuck in File land, use FileUtil.appendPath() and FileUtil.appendName() instead of new File(File,String).

To get out of File land, you have to start with getting FileLike for the directories you expect to find the files in. E.g. PipeRoot.getRootFileLike() instead of PipeRoot.getPath(). Also PipeRoot has useful resolve() methods and isUnderRoot() methods.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should have rewritten this whole class to be ExpDataFileLikeConverter and killed usages convert(File.class). Lets leave that cleanup for a separate PR.

@@ -218,49 +219,101 @@ public Object convert(Class type, Object value)
User user = (User) QueryService.get().getEnvironment(QueryService.Environment.USER);
Object fileRootPathObj = QueryService.get().getEnvironment(QueryService.Environment.FILEROOTPATH);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider putting a FileLike in the environment instead of a String? I don't love that we're using QueryService.get/setEnvironment() here, but that's a problem for another time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2nd thought. Can we refactor this so there is a public convert() method that takes (user, fileRootPath, assayFilesPath) and then have _convert() call that method with these values? Then we could have QUS and DataLoader call that method directly. Other usages could still use still rely on thread-local storage until/unless we remove this from ConvertUtils.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this to a static method that can be called directly. Currently there are 3 usages for importing assay results that could be refactored to use this static method. However, I didn't go ahead with the refactoring due to the much entangled run/result association. If we are removing this from ConvertUtils for sure then that's an effort worth trying. I've added a TODO comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes made to no longer register ExpDataFileConverter for File.class

if (!f.isAbsolute())
{
// Interpret relative paths based on the file root
f = new File(root.getRootPath(), f.getPath());
fileUnderRoot = new File(root.getRootPath(), f.getPath());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FileUtil.appendPath()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately switching to FileUtil.appendPath broke valid relative path such as "../@files/test.txt". But maybe we can just say this is not supported.

XingY added 2 commits July 30, 2025 14:47
# Conflicts:
#	study/test/src/org/labkey/test/tests/study/StudyDatasetFileFieldTest.java
@XingY XingY requested a review from labkey-matthewb August 4, 2025 15:31
Copy link
Contributor

@labkey-matthewb labkey-matthewb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I can track all the subclasses here, but this looks like a good step forward. I only have one question about files that exists on disk, but aren't in a known file root. Would getFileName() benefit from having a structured return value?

And the tests will be super helpful.

@@ -468,11 +465,48 @@ else if (renamedColumns.containsKey(name) && _columnInfoMap.containsKey(renamedC
ExceptionUtil.decorateException(e, ExceptionUtil.ExceptionInfo.SkipMothershipLogging, "true", true);
throw e;
}

// use File converter for known file fields even if inferTypes = false. If inferTypes, this is already done.
if (!getInferTypes())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is where I get lost. Why do we we rely on this? I would hope that import would always work with no conversion in DataLoader. (AFAIK, the only code that "should" rely on this is currently the infer type UI)

FileImageRenderHelper renderHelper = createRenderHelper(ctx, url, filename, displayName, fileIconUrl, popupIconUrl, thumbnail, isImage);
String displayName = getFileName(ctx, fileValue);
boolean unavailable = displayName.endsWith(UNAVAILABLE_FILE_SUFFIX);
String url = null == s || unavailable ? null : s.eval(ctx);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice check here. It would be nice if getFileName() had an explicit structured return (e.g. record(String fileDisplay,Boolean available), but this is workable. I wonder about the case in FileLinkDisplayColumn where we fall back to result = f.getName() when we don't find the file in an expected "file root".

What is the expected behavior in this case? Should we treat as "unavailable"? Should we render a URL?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of result = f.getName() when we fail to find file in root, valid is false, and the "unavailable" suffix will be added.

I added selenium test for grid and update view for LKS unavailable file both due to invalid path (exists but out side of file root) and absent file (file deleted after upload) in this commit

@XingY XingY merged commit 81f2c9e into develop Aug 5, 2025
8 of 9 checks passed
@XingY XingY deleted the fb_filePathIssues branch August 5, 2025 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants