Skip to content

Commit

Permalink
Relax related issue rules for restricted issues (#1249)
Browse files Browse the repository at this point in the history
  • Loading branch information
labkey-klum authored Feb 14, 2025
1 parent 7110a23 commit 50e6b2d
Show file tree
Hide file tree
Showing 2 changed files with 268 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,21 +96,6 @@ public boolean hasPermission(User user, @NotNull Issue issue, List<Issue> relate
return false;
}
}

// the user must also have access to all related issues
for (Issue related : relatedIssues)
{
Container relatedContainer = ContainerManager.getForId(related.getContainerId());
if (relatedContainer != null && isRestrictedIssueTracker(relatedContainer, related.getIssueDefName()))
{
Group relatedGroup = getRestrictedIssueListGroup(relatedContainer, related.getIssueDefName());
if (!checkAccess(user, related, relatedGroup))
{
errors.add(new SimpleValidationError(String.format("A related issue : %d is in a restricted issue list. You do not have access to that issue", related.getIssueId())));
return false;
}
}
}
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
package org.labkey.test.tests.onprc_ehr;

import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.labkey.test.BaseWebDriverTest;
import org.labkey.test.Locator;
import org.labkey.test.TestTimeoutException;
import org.labkey.test.categories.EHR;
import org.labkey.test.categories.ONPRC;
import org.labkey.test.pages.issues.DetailsPage;
import org.labkey.test.pages.issues.InsertPage;
import org.labkey.test.pages.issues.UpdatePage;
import org.labkey.test.util.IssuesHelper;
import org.labkey.test.util.SqlserverOnlyTest;
import org.labkey.test.util.TestUser;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

@Category({EHR.class, ONPRC.class})
public class ONPRC_RestrictedIssueTest extends BaseWebDriverTest implements SqlserverOnlyTest
{
private final IssuesHelper _issuesHelper;

// constants
private static final TestUser USER1 = new TestUser("user1_issuetest@issues.test");
private static final TestUser USER2 = new TestUser("user2_issuetest@issues.test");
private static final TestUser ISSUE_CREATOR = new TestUser("issue_creator_issuetest@issues.test");
private static final TestUser FOLDER_ADMIN = new TestUser("folder_admin_issuetest@issues.test");

private static final String RESTRICTED_ISSUES_LIST = "restricted-issues";
private static final String UNRESTRICTED_ISSUES_LIST = "unrestricted-issues";
private static final String ACCESS_ERROR_MSG = "This issue is in a restricted issue list. You do not have access to this issue";

public ONPRC_RestrictedIssueTest()
{
_issuesHelper = new IssuesHelper(this);
}

@BeforeClass
public static void doSetup()
{
ONPRC_RestrictedIssueTest initTest = getCurrentTest();
initTest.doInit();
}

public void doInit()
{
_containerHelper.createProject(getProjectName(), null);

// Create test users
USER1.create(this).addPermission("Editor", getProjectName());
USER2.create(this).addPermission("Editor", getProjectName());
ISSUE_CREATOR.create(this).addPermission("Editor", getProjectName());
FOLDER_ADMIN.create(this).addPermission("Folder Administrator", getProjectName());

// Add issue list definitions
_issuesHelper.createNewIssuesList(RESTRICTED_ISSUES_LIST, _containerHelper, true, false, false);
waitAndClickAndWait(Locator.linkContainingText(RESTRICTED_ISSUES_LIST));
_issuesHelper.goToAdmin();
_issuesHelper.setRestrictedIssueList(true);
_issuesHelper.setIssueAssignmentList("Site: Users");
clickButton("Save");

goToProjectHome();
_issuesHelper.createNewIssuesList(UNRESTRICTED_ISSUES_LIST, _containerHelper, false, false, false);
waitAndClickAndWait(Locator.linkContainingText(UNRESTRICTED_ISSUES_LIST));
_issuesHelper.goToAdmin();
_issuesHelper.setIssueAssignmentList("Site: Users");
clickButton("Save");
}

@Override
protected void doCleanup(boolean afterTest) throws TestTimeoutException
{
_userHelper.deleteUsers(false, USER1, USER2, ISSUE_CREATOR, FOLDER_ADMIN);
_containerHelper.deleteProject(getProjectName(), afterTest);
}

@Override
public List<String> getAssociatedModules()
{
return Arrays.asList("ehr", "onprc_ehr");
}

@Override
protected String getProjectName()
{
return "RestrictedIssuesVerifyProject";
}

@Test
public void restrictedIssueTest()
{
goToProjectHome();

// create a few issues in the restricted list
impersonate(ISSUE_CREATOR.getEmail());
clickAndWait(Locator.linkContainingText(RESTRICTED_ISSUES_LIST));
DetailsPage detailsPage = _issuesHelper.addIssue(String.format("Restricted issue assigned to (%s)", USER1.getUserDisplayName()), USER1.getUserDisplayName());
final String ISSUE_1 = detailsPage.getIssueId();
InsertPage insertPage = detailsPage.clickCreateNewIssue();
insertPage.title().set(String.format("Restricted issue assigned to (%s)", USER2.getUserDisplayName()));
insertPage.assignedTo().set(USER2.getUserDisplayName());
insertPage.save();
final String ISSUE_2 = insertPage.getIssueId();
stopImpersonating();

// verify site admins can see both issues (but not folder admins)
goToProjectHome();
clickAndWait(Locator.linkContainingText(RESTRICTED_ISSUES_LIST));
verifyIssueAccess(ISSUE_1, true);
verifyIssueAccess(ISSUE_2, true);

impersonate(FOLDER_ADMIN.getEmail());
goToProjectHome();
clickAndWait(Locator.linkContainingText(RESTRICTED_ISSUES_LIST));
verifyIssueAccess(ISSUE_1, false);
verifyIssueAccess(ISSUE_2, false);
stopImpersonating();

// creators can see all of the issues they opened
impersonate(ISSUE_CREATOR.getEmail());
goToProjectHome();
clickAndWait(Locator.linkContainingText(RESTRICTED_ISSUES_LIST));
verifyIssueAccess(ISSUE_1, true);
verifyIssueAccess(ISSUE_2, true);
stopImpersonating();

// users can view issues assigned to them
impersonate(USER1.getEmail());
goToProjectHome();
clickAndWait(Locator.linkContainingText(RESTRICTED_ISSUES_LIST));
verifyIssueAccess(ISSUE_1, true);
verifyIssueAccess(ISSUE_2, false);
stopImpersonating();

// verify notify list grants access
UpdatePage page = UpdatePage.beginAt(this, ISSUE_2);
page.notifyList().set(USER1.getEmail());
page.save();
impersonate(USER1.getEmail());
goToProjectHome();
clickAndWait(Locator.linkContainingText(RESTRICTED_ISSUES_LIST));
verifyIssueAccess(ISSUE_1, true);
verifyIssueAccess(ISSUE_2, true);
stopImpersonating();
}

private void verifyIssueAccess(String issueID, boolean shouldHaveAccess)
{
Locator issueLink = getIssueLinkLocator(issueID);
waitForElement(issueLink, defaultWaitForPage);
pushLocation();
waitAndClickAndWait(issueLink);
if (shouldHaveAccess)
assertTextNotPresent(ACCESS_ERROR_MSG);
else
assertTextPresent(ACCESS_ERROR_MSG);
popLocation();
}

@Test
public void relatedIssueTest()
{
goToProjectHome();

// create 2 issues related to each other
impersonate(ISSUE_CREATOR.getEmail());
clickAndWait(Locator.linkContainingText(RESTRICTED_ISSUES_LIST));
DetailsPage detailsPage = _issuesHelper.addIssue(String.format("Restricted issue assigned to (%s)", USER1.getUserDisplayName()), USER1.getUserDisplayName());
final String ISSUE_1 = detailsPage.getIssueId();
InsertPage insertPage = detailsPage.clickCreateNewIssue();
insertPage.title().set(String.format("Restricted issue assigned to (%s)", USER2.getUserDisplayName()));
insertPage.assignedTo().set(USER2.getUserDisplayName());
insertPage.related().set(ISSUE_1);
insertPage.save();
final String ISSUE_2 = insertPage.getIssueId();
stopImpersonating();

// verify creator sees both all issues and their relationships
impersonate(ISSUE_CREATOR.getEmail());
goToProjectHome();
clickAndWait(Locator.linkContainingText(RESTRICTED_ISSUES_LIST));
verifyRelatedIssueAccess(ISSUE_1, ISSUE_2, true);
verifyRelatedIssueAccess(ISSUE_2, ISSUE_1, true);
stopImpersonating();

// verify users can open issue assigned to them but not see the related issue
impersonate(USER1.getEmail());
goToProjectHome();
clickAndWait(Locator.linkContainingText(RESTRICTED_ISSUES_LIST));
verifyRelatedIssueAccess(ISSUE_1, ISSUE_2, false);
// shouldn't be able to access the other issue at all
verifyIssueAccess(ISSUE_2, false);
stopImpersonating();

impersonate(USER2.getEmail());
goToProjectHome();
clickAndWait(Locator.linkContainingText(RESTRICTED_ISSUES_LIST));
verifyRelatedIssueAccess(ISSUE_2, ISSUE_1, false);
verifyIssueAccess(ISSUE_1, false);
stopImpersonating();

// create issues in the unrestricted issue list and relate them to issues in the other list
impersonate(ISSUE_CREATOR.getEmail());
goToProjectHome();
clickAndWait(Locator.linkContainingText(UNRESTRICTED_ISSUES_LIST));
// issue related to both restricted issues
detailsPage = _issuesHelper.addIssue(String.format("UnRestricted issue assigned to (%s)", USER1.getUserDisplayName()),
USER1.getUserDisplayName(), Collections.singletonMap("related", String.join(",", List.of(ISSUE_1, ISSUE_2))));
final String ISSUE_3 = detailsPage.getIssueId();
stopImpersonating();

// any user with read access to the unrestricted list can see details but no links to the linked restricted issues
impersonate(FOLDER_ADMIN.getEmail());
goToProjectHome();
clickAndWait(Locator.linkContainingText(UNRESTRICTED_ISSUES_LIST));
verifyRelatedIssueAccess(ISSUE_3, ISSUE_1, false);
verifyRelatedIssueAccess(ISSUE_3, ISSUE_2, false);
stopImpersonating();

// users can link to the restricted issues they have access to
impersonate(USER1.getEmail());
goToProjectHome();
clickAndWait(Locator.linkContainingText(UNRESTRICTED_ISSUES_LIST));
verifyRelatedIssueAccess(ISSUE_3, ISSUE_1, true);
verifyRelatedIssueAccess(ISSUE_3, ISSUE_2, false);
stopImpersonating();

impersonate(USER2.getEmail());
goToProjectHome();
clickAndWait(Locator.linkContainingText(UNRESTRICTED_ISSUES_LIST));
verifyRelatedIssueAccess(ISSUE_3, ISSUE_1, false);
verifyRelatedIssueAccess(ISSUE_3, ISSUE_2, true);
stopImpersonating();
}

private void verifyRelatedIssueAccess(String issueID, String relatedIssueID, boolean shouldHaveRelatedAccess)
{
Locator issueLink = getIssueLinkLocator(issueID);
waitForElement(issueLink, defaultWaitForPage);
pushLocation();
clickAndWait(issueLink);
Locator relatedIssueLink = getIssueLinkLocator(relatedIssueID);
if (shouldHaveRelatedAccess)
{
assertElementPresent(relatedIssueLink);
// related link should also navigate properly
clickAndWait(relatedIssueLink);
assertTextNotPresent(ACCESS_ERROR_MSG);
}
else
{
// no link but the related ID should render as text
assertElementNotPresent(relatedIssueLink);
assertTextPresent(relatedIssueID);
}
popLocation();
}

private Locator getIssueLinkLocator(String issueID)
{
return Locator.tagWithAttributeContaining("a", "href", String.format("issues-details.view?issueId=%s", issueID));
}
}

0 comments on commit 50e6b2d

Please sign in to comment.