From 0656dda85d1b33176bc57bae702ef10c4097964a Mon Sep 17 00:00:00 2001 From: Lum Date: Tue, 27 Oct 2026 17:09:43 -0700 Subject: [PATCH 1/3] regression tests for study report actions. --- .../labkey/test/tests/DataReportsTest.java | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/tests/DataReportsTest.java b/src/org/labkey/test/tests/DataReportsTest.java index 8d73c0beed..08431e3534 100644 --- a/src/org/labkey/test/tests/DataReportsTest.java +++ b/src/org/labkey/test/tests/DataReportsTest.java @@ -15,12 +15,16 @@ */ package org.labkey.test.tests; +import org.apache.hc.core5.http.HttpStatus; import org.jetbrains.annotations.NotNull; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.labkey.remoteapi.CommandException; +import org.labkey.remoteapi.Connection; +import org.labkey.remoteapi.SimplePostCommand; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.SortDirection; @@ -38,13 +42,20 @@ import org.labkey.test.util.PermissionsHelper; import org.labkey.test.util.RReportHelper; import org.labkey.test.util.ScriptInvocationLogHelper; +import org.labkey.test.util.TestUser; import org.openqa.selenium.WebElement; import java.io.File; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.labkey.test.util.PermissionsHelper.AUTHOR_ROLE; import static org.labkey.test.util.PermissionsHelper.EDITOR_ROLE; import static org.labkey.test.util.PermissionsHelper.PROJECT_ADMIN_ROLE; @@ -130,13 +141,15 @@ private String R_SCRIPT3(String database, String colName) protected final static String R_USER = "r_editor@report.test"; protected final static String AUTHOR_USER = "author_user@report.test"; + private final static String SCRIPT_READER_USER = "script_reader@report.test"; + private final static String SCRIPT_EDITOR_USER = "script_editor@report.test"; private final ApiPermissionsHelper _apiPermissionsHelper = new ApiPermissionsHelper(this); @Override protected void doCleanup(boolean afterTest) throws TestTimeoutException { - _userHelper.deleteUsers(false, R_USER, AUTHOR_USER); + _userHelper.deleteUsers(false, R_USER, AUTHOR_USER, SCRIPT_READER_USER, SCRIPT_EDITOR_USER); super.doCleanup(afterTest); } @@ -549,6 +562,45 @@ public void testScriptInvocationLogging() throws Exception ScriptInvocationLogHelper.assertScriptLogged((WebDriverWrapper) this, "done", "report id=", reportName); } + @Test + public void testStudyActionsRejectNewScriptReports() throws Exception + { + TestUser reader = new TestUser(SCRIPT_READER_USER).create(this).setInitialPassword().addPermission(READER_ROLE, getProjectName()); + TestUser editor = new TestUser(SCRIPT_EDITOR_USER).create(this).setInitialPassword().addPermission(EDITOR_ROLE, getProjectName()); + + log("Reader saves a private R report through saveReportView"); + Map privateReport = scriptReportParams("Reader R report"); + privateReport.put("shareReport", false); + assertScriptReportRejected(reader.getUserConnection(), "saveReportView", privateReport); + + log("Editor saves a shared R report through saveReport"); + assertScriptReportRejected(editor.getUserConnection(), "saveReport", scriptReportParams("Editor R report")); + } + + private Map scriptReportParams(String label) + { + Map params = new HashMap<>(); + params.put("reportType", "Study.rReport"); + params.put("label", label); + params.put("params", "script=" + URLEncoder.encode("print('GH Issue 1682')", StandardCharsets.UTF_8)); + return params; + } + + private void assertScriptReportRejected(Connection connection, String action, Map params) throws IOException + { + SimplePostCommand command = new SimplePostCommand("study-reports", action); + command.setParameters(params); + try + { + command.execute(connection, getProjectName() + "/" + getFolderName()); + fail("Non-developer saved a new R report through study-reports-" + action); + } + catch (CommandException e) + { + assertEquals("Wrong status saving R report through study-reports-" + action, HttpStatus.SC_FORBIDDEN, e.getStatusCode()); + } + } + /**create an R report from the dataset page * * @param name name of script From 64584a40f860f1b08de75d0cb2b08ae7cebc9745 Mon Sep 17 00:00:00 2001 From: Lum Date: Mon, 2 Nov 2026 15:23:34 -0800 Subject: [PATCH 2/3] Restrict report types and raise permission for the study SaveReportAction. --- .../labkey/test/tests/DataReportsTest.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/org/labkey/test/tests/DataReportsTest.java b/src/org/labkey/test/tests/DataReportsTest.java index 08431e3534..53c209f656 100644 --- a/src/org/labkey/test/tests/DataReportsTest.java +++ b/src/org/labkey/test/tests/DataReportsTest.java @@ -23,6 +23,7 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.labkey.remoteapi.CommandException; +import org.labkey.remoteapi.CommandResponse; import org.labkey.remoteapi.Connection; import org.labkey.remoteapi.SimplePostCommand; import org.labkey.test.BaseWebDriverTest; @@ -571,10 +572,19 @@ public void testStudyActionsRejectNewScriptReports() throws Exception log("Reader saves a private R report through saveReportView"); Map privateReport = scriptReportParams("Reader R report"); privateReport.put("shareReport", false); - assertScriptReportRejected(reader.getUserConnection(), "saveReportView", privateReport); - - log("Editor saves a shared R report through saveReport"); - assertScriptReportRejected(editor.getUserConnection(), "saveReport", scriptReportParams("Editor R report")); + assertScriptReportRejected(reader.getUserConnection(), "saveReportView", privateReport, HttpStatus.SC_FORBIDDEN); + + log("Editor saves a shared R report through saveReportView"); + Map sharedReport = scriptReportParams("Editor R report"); + sharedReport.put("shareReport", true); + assertScriptReportRejected(editor.getUserConnection(), "saveReportView", sharedReport, HttpStatus.SC_FORBIDDEN); + + log("saveReport requires admin and accepts only dataset grid views"); + assertScriptReportRejected(editor.getUserConnection(), "saveReport", scriptReportParams("Editor R report"), HttpStatus.SC_FORBIDDEN); + SimplePostCommand command = new SimplePostCommand("study-reports", "saveReport"); + command.setParameters(scriptReportParams("Admin R report")); + CommandResponse response = command.execute(createDefaultConnection(), getProjectName() + "/" + getFolderName()); + assertTrue("saveReport didn't reject an R report:\n" + response.getText(), response.getText().contains("Unsupported report type: Study.rReport")); } private Map scriptReportParams(String label) @@ -586,7 +596,7 @@ private Map scriptReportParams(String label) return params; } - private void assertScriptReportRejected(Connection connection, String action, Map params) throws IOException + private void assertScriptReportRejected(Connection connection, String action, Map params, int expectedStatus) throws IOException { SimplePostCommand command = new SimplePostCommand("study-reports", action); command.setParameters(params); @@ -597,7 +607,7 @@ private void assertScriptReportRejected(Connection connection, String action, Ma } catch (CommandException e) { - assertEquals("Wrong status saving R report through study-reports-" + action, HttpStatus.SC_FORBIDDEN, e.getStatusCode()); + assertEquals("Wrong status saving R report through study-reports-" + action, expectedStatus, e.getStatusCode()); } } From 31b7ca93758b11c6e52d7c8b49b1d5bb8bf8f5f4 Mon Sep 17 00:00:00 2001 From: Lum Date: Mon, 2 Nov 2026 16:22:25 -0800 Subject: [PATCH 3/3] restrict the study saveReportView to crosstab reports --- .../labkey/test/tests/DataReportsTest.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/org/labkey/test/tests/DataReportsTest.java b/src/org/labkey/test/tests/DataReportsTest.java index 53c209f656..5bb43399c5 100644 --- a/src/org/labkey/test/tests/DataReportsTest.java +++ b/src/org/labkey/test/tests/DataReportsTest.java @@ -569,22 +569,18 @@ public void testStudyActionsRejectNewScriptReports() throws Exception TestUser reader = new TestUser(SCRIPT_READER_USER).create(this).setInitialPassword().addPermission(READER_ROLE, getProjectName()); TestUser editor = new TestUser(SCRIPT_EDITOR_USER).create(this).setInitialPassword().addPermission(EDITOR_ROLE, getProjectName()); - log("Reader saves a private R report through saveReportView"); + log("saveReportView accepts only crosstab reports"); Map privateReport = scriptReportParams("Reader R report"); privateReport.put("shareReport", false); - assertScriptReportRejected(reader.getUserConnection(), "saveReportView", privateReport, HttpStatus.SC_FORBIDDEN); + assertUnsupportedReportType(reader.getUserConnection(), "saveReportView", privateReport); - log("Editor saves a shared R report through saveReportView"); Map sharedReport = scriptReportParams("Editor R report"); sharedReport.put("shareReport", true); - assertScriptReportRejected(editor.getUserConnection(), "saveReportView", sharedReport, HttpStatus.SC_FORBIDDEN); + assertUnsupportedReportType(editor.getUserConnection(), "saveReportView", sharedReport); log("saveReport requires admin and accepts only dataset grid views"); assertScriptReportRejected(editor.getUserConnection(), "saveReport", scriptReportParams("Editor R report"), HttpStatus.SC_FORBIDDEN); - SimplePostCommand command = new SimplePostCommand("study-reports", "saveReport"); - command.setParameters(scriptReportParams("Admin R report")); - CommandResponse response = command.execute(createDefaultConnection(), getProjectName() + "/" + getFolderName()); - assertTrue("saveReport didn't reject an R report:\n" + response.getText(), response.getText().contains("Unsupported report type: Study.rReport")); + assertUnsupportedReportType(createDefaultConnection(), "saveReport", scriptReportParams("Admin R report")); } private Map scriptReportParams(String label) @@ -596,6 +592,15 @@ private Map scriptReportParams(String label) return params; } + private void assertUnsupportedReportType(Connection connection, String action, Map params) throws IOException, CommandException + { + SimplePostCommand command = new SimplePostCommand("study-reports", action); + command.setParameters(params); + CommandResponse response = command.execute(connection, getProjectName() + "/" + getFolderName()); + assertTrue("study-reports-" + action + " didn't reject an R report:\n" + response.getText(), + response.getText().contains("Unsupported report type: Study.rReport")); + } + private void assertScriptReportRejected(Connection connection, String action, Map params, int expectedStatus) throws IOException { SimplePostCommand command = new SimplePostCommand("study-reports", action);