From b337d2d0292ac28cc5d7a61fe2d982c050323f72 Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Fri, 11 Apr 2025 14:07:21 -0700 Subject: [PATCH 1/3] FileLike conversion in FeeScheduleController --- .../controllers/FeeScheduleController.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/snprc_ehr/src/org/labkey/snprc_ehr/controllers/FeeScheduleController.java b/snprc_ehr/src/org/labkey/snprc_ehr/controllers/FeeScheduleController.java index cd8512f6f..70d0b304b 100644 --- a/snprc_ehr/src/org/labkey/snprc_ehr/controllers/FeeScheduleController.java +++ b/snprc_ehr/src/org/labkey/snprc_ehr/controllers/FeeScheduleController.java @@ -36,6 +36,8 @@ import org.labkey.snprc_ehr.pipeline.FeeScheduleExcelParser; import org.labkey.snprc_ehr.pipeline.FeeScheduleImportForm; import org.labkey.snprc_ehr.pipeline.FeeSchedulePipelineJob; +import org.labkey.vfs.FileLike; +import org.labkey.vfs.FileSystemLike; import org.springframework.validation.BindException; import org.springframework.validation.Errors; import org.springframework.web.servlet.ModelAndView; @@ -101,6 +103,7 @@ public ModelAndView getView(FeeScheduleImportForm form, boolean reshow, BindExce { String fileSize; String dateString; + File fileToVerify = null; File file = null; SimpleDateFormat formatString = new SimpleDateFormat("MM/dd/yyyy hh:mm"); @@ -108,7 +111,19 @@ public ModelAndView getView(FeeScheduleImportForm form, boolean reshow, BindExce FeeScheduleExcelParser fsep; if (form.getFilePath() != null) { - file = new File(form.getFilePath()); + //file path here is constructed from the pipeline, and contains pipeline root + actual file. + fileToVerify = new File(form.getFilePath()); + + PipeRoot pipeRoot = PipelineService.get().findPipelineRoot(getContainer()); + if (pipeRoot == null) + { + throw new PipelineJobException("Could not find a pipeline root for '" + getContainer().getPath() + "'"); + } + FileLike rootFileLike = pipeRoot.getRootFileLike(); + if (rootFileLike.isDescendant(fileToVerify.toURI())) + { + file = FileSystemLike.toFile(rootFileLike.resolveChild(fileToVerify.getName())); + } } if (file != null && file.exists()) { From 6bbbd1896ed2f292597bb86c3f7dc347f15cfe94 Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Fri, 11 Apr 2025 15:12:15 -0700 Subject: [PATCH 2/3] Relativize file path --- .../controllers/FeeScheduleController.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/snprc_ehr/src/org/labkey/snprc_ehr/controllers/FeeScheduleController.java b/snprc_ehr/src/org/labkey/snprc_ehr/controllers/FeeScheduleController.java index 70d0b304b..7efd5249a 100644 --- a/snprc_ehr/src/org/labkey/snprc_ehr/controllers/FeeScheduleController.java +++ b/snprc_ehr/src/org/labkey/snprc_ehr/controllers/FeeScheduleController.java @@ -29,6 +29,8 @@ import org.labkey.api.security.RequiresPermission; import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.util.PageFlowUtil; +import org.labkey.api.util.Path; +import org.labkey.api.util.URIUtil; import org.labkey.api.util.URLHelper; import org.labkey.api.view.ActionURL; import org.labkey.api.view.JspView; @@ -43,6 +45,7 @@ import org.springframework.web.servlet.ModelAndView; import java.io.File; +import java.net.URI; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; @@ -111,7 +114,7 @@ public ModelAndView getView(FeeScheduleImportForm form, boolean reshow, BindExce FeeScheduleExcelParser fsep; if (form.getFilePath() != null) { - //file path here is constructed from the pipeline, and contains pipeline root + actual file. + //file path here is constructed from the pipeline, and contains pipeline root + file. fileToVerify = new File(form.getFilePath()); PipeRoot pipeRoot = PipelineService.get().findPipelineRoot(getContainer()); @@ -119,10 +122,22 @@ public ModelAndView getView(FeeScheduleImportForm form, boolean reshow, BindExce { throw new PipelineJobException("Could not find a pipeline root for '" + getContainer().getPath() + "'"); } - FileLike rootFileLike = pipeRoot.getRootFileLike(); - if (rootFileLike.isDescendant(fileToVerify.toURI())) + + FileLike allowedRoot = pipeRoot.getRootFileLike(); + URI relativeURI = URIUtil.relativize(allowedRoot.toURI(), fileToVerify.toURI()); // if root = /a/b/c/ and file = /a/b/c/d/e/f.xlsx, relativeURI = d/e/f.xlsx + + if (relativeURI == null) + { + throw new IllegalArgumentException("File '" + fileToVerify.toURI().getPath() + "' is outside the allowed root '" + allowedRoot.toURI().getPath() + "'"); + } + + if (allowedRoot.isDescendant(fileToVerify.toURI())) // if root = /a/b/c/ and file = /a/b/c/d/e/f.xlsx - among other things, this essentially checks if '/a/b/c/d/e/f.xlsx' starts with '/a/b/c/' + { + file = FileSystemLike.toFile(allowedRoot.resolveFile(new Path(relativeURI.getPath()))); + } + else { - file = FileSystemLike.toFile(rootFileLike.resolveChild(fileToVerify.getName())); + throw new IllegalArgumentException("File '" + relativeURI.getPath() + "' is not a descendent of '" + allowedRoot.toURI().getPath() + "'"); } } if (file != null && file.exists()) From c8ab46a1b81b31327465f3f7c16dcb6d6f9491fe Mon Sep 17 00:00:00 2001 From: Binal Patel Date: Mon, 14 Apr 2025 09:03:18 -0700 Subject: [PATCH 3/3] setLogFile() with FileLike. Use FileLike in getView(), validateCommand(), and handlePost(). --- .../controllers/FeeScheduleController.java | 62 ++++++++++--------- .../pipeline/FeeSchedulePipelineJob.java | 6 +- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/snprc_ehr/src/org/labkey/snprc_ehr/controllers/FeeScheduleController.java b/snprc_ehr/src/org/labkey/snprc_ehr/controllers/FeeScheduleController.java index 7efd5249a..dc85e57c2 100644 --- a/snprc_ehr/src/org/labkey/snprc_ehr/controllers/FeeScheduleController.java +++ b/snprc_ehr/src/org/labkey/snprc_ehr/controllers/FeeScheduleController.java @@ -35,6 +35,7 @@ import org.labkey.api.view.ActionURL; import org.labkey.api.view.JspView; import org.labkey.api.view.NavTree; +import org.labkey.api.view.NotFoundException; import org.labkey.snprc_ehr.pipeline.FeeScheduleExcelParser; import org.labkey.snprc_ehr.pipeline.FeeScheduleImportForm; import org.labkey.snprc_ehr.pipeline.FeeSchedulePipelineJob; @@ -106,7 +107,6 @@ public ModelAndView getView(FeeScheduleImportForm form, boolean reshow, BindExce { String fileSize; String dateString; - File fileToVerify = null; File file = null; SimpleDateFormat formatString = new SimpleDateFormat("MM/dd/yyyy hh:mm"); @@ -115,30 +115,7 @@ public ModelAndView getView(FeeScheduleImportForm form, boolean reshow, BindExce if (form.getFilePath() != null) { //file path here is constructed from the pipeline, and contains pipeline root + file. - fileToVerify = new File(form.getFilePath()); - - PipeRoot pipeRoot = PipelineService.get().findPipelineRoot(getContainer()); - if (pipeRoot == null) - { - throw new PipelineJobException("Could not find a pipeline root for '" + getContainer().getPath() + "'"); - } - - FileLike allowedRoot = pipeRoot.getRootFileLike(); - URI relativeURI = URIUtil.relativize(allowedRoot.toURI(), fileToVerify.toURI()); // if root = /a/b/c/ and file = /a/b/c/d/e/f.xlsx, relativeURI = d/e/f.xlsx - - if (relativeURI == null) - { - throw new IllegalArgumentException("File '" + fileToVerify.toURI().getPath() + "' is outside the allowed root '" + allowedRoot.toURI().getPath() + "'"); - } - - if (allowedRoot.isDescendant(fileToVerify.toURI())) // if root = /a/b/c/ and file = /a/b/c/d/e/f.xlsx - among other things, this essentially checks if '/a/b/c/d/e/f.xlsx' starts with '/a/b/c/' - { - file = FileSystemLike.toFile(allowedRoot.resolveFile(new Path(relativeURI.getPath()))); - } - else - { - throw new IllegalArgumentException("File '" + relativeURI.getPath() + "' is not a descendent of '" + allowedRoot.toURI().getPath() + "'"); - } + file = getVerifiedFile(form.getFilePath()); } if (file != null && file.exists()) { @@ -176,16 +153,45 @@ public ModelAndView getView(FeeScheduleImportForm form, boolean reshow, BindExce return new JspView<>("/org/labkey/snprc_ehr/pipeline/FeeScheduleImport.jsp", form, errors); } + private File getVerifiedFile(String filePath) + { + File fileToVerify = new File(filePath); + + PipeRoot pipeRoot = PipelineService.get().findPipelineRoot(getContainer()); + if (pipeRoot == null) + { + throw new NotFoundException("Could not find a pipeline root for '" + getContainer().getPath() + "'"); + } + + FileLike allowedRoot = pipeRoot.getRootFileLike(); + URI relativeURI = URIUtil.relativize(allowedRoot.toURI(), fileToVerify.toURI()); // if root = /a/b/c/ and file = /a/b/c/d/e/f.xlsx, relativeURI = d/e/f.xlsx + + if (relativeURI == null) + { + throw new IllegalArgumentException("File '" + fileToVerify.toURI().getPath() + "' is outside the allowed root '" + allowedRoot.toURI().getPath() + "'"); + } + + if (allowedRoot.isDescendant(fileToVerify.toURI())) // if root = /a/b/c/ and file = /a/b/c/d/e/f.xlsx - among other things, this essentially checks if '/a/b/c/d/e/f.xlsx' starts with '/a/b/c/' + { + return FileSystemLike.toFile(allowedRoot.resolveFile(new Path(relativeURI.getPath()))); + } + else + { + throw new IllegalArgumentException("File '" + relativeURI.getPath() + "' is not a descendent of '" + allowedRoot.toURI().getPath() + "'"); + } + } + @Override public void validateCommand(FeeScheduleImportForm form, Errors errors) { - _fsFile = new File(form.getFilePath()); + _fsFile = getVerifiedFile(form.getFilePath()); // Be sure that the referenced file exists - if (_fsFile == null || !_fsFile.exists()) + if (!_fsFile.exists()) { errors.reject(ERROR_MSG, "Could not find file at path: " + form.getFilePath()); } } + @Override public boolean handlePost(FeeScheduleImportForm form, BindException errors) { @@ -193,7 +199,7 @@ public boolean handlePost(FeeScheduleImportForm form, BindException errors) try { PipeRoot pipelineRoot = PipelineService.get().findPipelineRoot(getContainer()); - PipelineService.get().queueJob(new FeeSchedulePipelineJob(getContainer(), getUser(), getViewContext().getActionURL(), pipelineRoot, _fsFile, form)); + PipelineService.get().queueJob(new FeeSchedulePipelineJob(getContainer(), getUser(), getViewContext().getActionURL(), pipelineRoot, getVerifiedFile(_fsFile.getAbsolutePath()), form)); _url = PageFlowUtil.urlProvider(PipelineStatusUrls.class).urlBegin(getContainer().getProject()); _url.addParameter("StatusFiles.containerFilterName", ContainerFilter.Type.CurrentAndSubfolders.name()); diff --git a/snprc_ehr/src/org/labkey/snprc_ehr/pipeline/FeeSchedulePipelineJob.java b/snprc_ehr/src/org/labkey/snprc_ehr/pipeline/FeeSchedulePipelineJob.java index 57b3d79e0..ef08d2c19 100644 --- a/snprc_ehr/src/org/labkey/snprc_ehr/pipeline/FeeSchedulePipelineJob.java +++ b/snprc_ehr/src/org/labkey/snprc_ehr/pipeline/FeeSchedulePipelineJob.java @@ -22,9 +22,12 @@ import org.labkey.api.pipeline.PipelineJobException; import org.labkey.api.security.User; import org.labkey.api.util.FileUtil; +import org.labkey.api.util.Path; import org.labkey.api.util.URLHelper; import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewBackgroundInfo; +import org.labkey.vfs.FileLike; +import org.labkey.vfs.FileSystemLike; import java.io.File; @@ -47,7 +50,8 @@ public FeeSchedulePipelineJob(Container c, User user, ActionURL url, PipeRoot pi { super(null, new ViewBackgroundInfo(c, user, url), pipeRoot); _importFile = importFile; - setLogFile(new File(pipeRoot.getRootPath(), FileUtil.makeFileNameWithTimestamp("FeeSchedulePipeline", "log"))); + FileLike logFile = pipeRoot.getLogDirectoryFileLike(true).resolveChild(FileUtil.makeFileNameWithTimestamp("FeeSchedulePipeline", "log")); + setLogFile(logFile.toNioPathForWrite()); _form = form; _container = c; _user = user;