|
1 | 1 | package org.labkey.test.tests; |
2 | 2 |
|
| 3 | +import org.apache.hc.core5.http.HttpStatus; |
3 | 4 | import org.assertj.core.api.Assertions; |
4 | 5 | import org.jetbrains.annotations.Nullable; |
5 | 6 | import org.junit.Assert; |
|
9 | 10 | import org.labkey.test.BaseWebDriverTest; |
10 | 11 | import org.labkey.test.Locator; |
11 | 12 | import org.labkey.test.TestFileUtils; |
| 13 | +import org.labkey.test.WebTestHelper; |
12 | 14 | import org.labkey.test.categories.Daily; |
13 | 15 | import org.labkey.test.components.DomainDesignerPage; |
14 | 16 | import org.labkey.test.components.domain.DomainFieldRow; |
15 | 17 | import org.labkey.test.components.domain.DomainFormPanel; |
| 18 | +import org.labkey.test.pages.ReactAssayDesignerPage; |
16 | 19 | import org.labkey.test.pages.experiment.UpdateSampleTypePage; |
17 | 20 | import org.labkey.test.params.FieldDefinition; |
18 | 21 | import org.labkey.test.params.experiment.SampleTypeDefinition; |
| 22 | +import org.labkey.test.util.ApiPermissionsHelper; |
19 | 23 | import org.labkey.test.util.DataRegionTable; |
| 24 | +import org.labkey.test.util.PasswordUtil; |
| 25 | +import org.labkey.test.util.PermissionsHelper; |
20 | 26 | import org.labkey.test.util.PortalHelper; |
21 | 27 | import org.labkey.test.util.SampleTypeHelper; |
22 | 28 | import org.labkey.test.util.TestDataGenerator; |
23 | 29 |
|
| 30 | +import org.openqa.selenium.By; |
| 31 | +import org.openqa.selenium.support.ui.ExpectedConditions; |
| 32 | + |
24 | 33 | import java.io.File; |
| 34 | +import java.net.URI; |
25 | 35 | import java.util.List; |
26 | 36 |
|
27 | 37 | @Category({Daily.class}) |
28 | 38 | @BaseWebDriverTest.ClassTimeout(minutes = 2) |
29 | 39 | public class AttachmentFieldTest extends BaseWebDriverTest |
30 | 40 | { |
| 41 | + private static final String RESTRICTED_PROJECT = "AttachmentFieldTest Restricted Project"; |
| 42 | + private static final String RESTRICTED_USER = "[email protected]"; |
31 | 43 | private final File SAMPLE_FILE = new File(TestFileUtils.getSampleData("fileTypes"), "jpg_sample.jpg"); |
32 | 44 |
|
33 | 45 | @BeforeClass |
@@ -57,6 +69,14 @@ private void doSetup() |
57 | 69 | portalHelper.addBodyWebPart("Lists"); |
58 | 70 | } |
59 | 71 |
|
| 72 | + @Override |
| 73 | + protected void doCleanup(boolean afterTest) |
| 74 | + { |
| 75 | + super.doCleanup(afterTest); |
| 76 | + _containerHelper.deleteProject(RESTRICTED_PROJECT, false); |
| 77 | + _userHelper.deleteUsers(false, RESTRICTED_USER); |
| 78 | + } |
| 79 | + |
60 | 80 | @Test |
61 | 81 | public void testFileFieldInSampleType() |
62 | 82 | { |
@@ -138,4 +158,63 @@ public void testAttachmentFieldInLists() |
138 | 158 | File downloadedFile = doAndWaitForDownload(() -> Locator.tagWithAttributeContaining("img", "title", SAMPLE_FILE.getName()).findElement(getDriver()).click()); |
139 | 159 | Assert.assertTrue("Downloaded file is empty", downloadedFile.length() > 0); |
140 | 160 | } |
| 161 | + |
| 162 | + // Kanban #1924 |
| 163 | + @Test |
| 164 | + public void testDownloadFileLinkCrossContainerPermission() |
| 165 | + { |
| 166 | + final String assayName = "CrossContainerAssay"; |
| 167 | + final String runFieldName = "runFile"; |
| 168 | + |
| 169 | + log("Create restricted project with Assay folder type to provide a pipeline root for file storage"); |
| 170 | + _containerHelper.createProject(RESTRICTED_PROJECT, "Assay"); |
| 171 | + |
| 172 | + log("Create a General assay with a run-level file link field"); |
| 173 | + goToProjectHome(RESTRICTED_PROJECT); |
| 174 | + goToManageAssays(); |
| 175 | + ReactAssayDesignerPage assayDesigner = _assayHelper.createAssayDesign("General", assayName); |
| 176 | + assayDesigner.setEditableRuns(true); |
| 177 | + assayDesigner.goToRunFields().addField(runFieldName).setType(FieldDefinition.ColumnType.File); |
| 178 | + assayDesigner.clickFinish(); |
| 179 | + |
| 180 | + log("Import a minimal assay run"); |
| 181 | + clickAndWait(Locator.linkWithText(assayName)); |
| 182 | + clickButton("Import Data"); |
| 183 | + clickButton("Next"); |
| 184 | + setFormElement(Locator.name("name"), "TestRun"); |
| 185 | + setFormElement(Locator.name("TextAreaDataCollector.textArea"), |
| 186 | + "Specimen ID\tParticipant ID\tVisit ID\n100\t1A2B\t1"); |
| 187 | + clickButton("Save and Finish"); |
| 188 | + |
| 189 | + log("Edit the run to set the file field"); |
| 190 | + clickAndWait(Locator.linkWithText("view runs")); |
| 191 | + new DataRegionTable("Runs", getDriver()).clickEditRow(0); |
| 192 | + setFormElement(Locator.name("quf_" + runFieldName), SAMPLE_FILE); |
| 193 | + clickButton("Submit"); |
| 194 | + waitForElement(DataRegionTable.updateLinkLocator()); |
| 195 | + |
| 196 | + log("Hover over the run file thumbnail to reveal the popup and get the objectURI-based downloadFileLink URL"); |
| 197 | + mouseOver(Locator.xpath("//img[contains(@title, '" + SAMPLE_FILE.getName() + "')]")); |
| 198 | + longWait().until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("#helpDiv"))); |
| 199 | + String restrictedDownloadUrl = Locator.xpath("//div[@id='helpDiv']//img[contains(@src, 'downloadFileLink')]") |
| 200 | + .findElement(getDriver()).getAttribute("src"); |
| 201 | + Assertions.assertThat(restrictedDownloadUrl).as("Expected downloadFileLink URL with objectURI parameter") |
| 202 | + .contains("downloadFileLink") |
| 203 | + .contains("objectURI"); |
| 204 | + |
| 205 | + // Build a cross-container URL: keep the same objectURI (run LSID) and propertyId but use the main project's |
| 206 | + // container. |
| 207 | + String crossContainerUrl = WebTestHelper.buildURL("core", getProjectName(), "downloadFileLink") |
| 208 | + + "?" + URI.create(restrictedDownloadUrl).getRawQuery(); |
| 209 | + |
| 210 | + log("Create a reader user with access to the main project only, not to the restricted project"); |
| 211 | + _userHelper.createUser(RESTRICTED_USER); |
| 212 | + _userHelper.setInitialPassword(RESTRICTED_USER); |
| 213 | + new ApiPermissionsHelper(this).addMemberToRole(RESTRICTED_USER, "Reader", PermissionsHelper.MemberType.user, getProjectName()); |
| 214 | + |
| 215 | + log("Verify cross-container download is rejected with 403 when user lacks read permission on the object's container"); |
| 216 | + int status = WebTestHelper.getHttpResponse(crossContainerUrl, RESTRICTED_USER, PasswordUtil.getPassword()).getResponseCode(); |
| 217 | + Assert.assertEquals("Expected 403 Forbidden when user lacks read permission on the object's container", |
| 218 | + HttpStatus.SC_FORBIDDEN, status); |
| 219 | + } |
141 | 220 | } |
0 commit comments