diff --git a/api/src/org/labkey/api/view/ViewServlet.java b/api/src/org/labkey/api/view/ViewServlet.java index a30b3f22e22..90f12c6930e 100644 --- a/api/src/org/labkey/api/view/ViewServlet.java +++ b/api/src/org/labkey/api/view/ViewServlet.java @@ -525,6 +525,12 @@ public String getParameter(@NotNull String name) { return _actionURL.getParameterNames(); } + + @Override + public @Nullable String getQueryString() + { + return null == _actionURL || _actionURL.getParameters().isEmpty() ? null : _actionURL.getQueryString(); + } } diff --git a/api/src/org/labkey/api/view/WebPartFactory.java b/api/src/org/labkey/api/view/WebPartFactory.java index a5dd66bcc44..52419f92b07 100644 --- a/api/src/org/labkey/api/view/WebPartFactory.java +++ b/api/src/org/labkey/api/view/WebPartFactory.java @@ -77,7 +77,7 @@ public interface WebPartFactory void setModule(Module module); - /** For backwards compatibility, names that this web part might have been previously called and should still match it for existing portal configurations */ + /** For backwards compatibility, names that this web part might have been previously called and should still match for existing portal configurations */ List getLegacyNames(); boolean isAvailable(Container c, String scope, String location); diff --git a/wiki/src/org/labkey/wiki/WikiManager.java b/wiki/src/org/labkey/wiki/WikiManager.java index bccca39a0ee..65ee016adef 100644 --- a/wiki/src/org/labkey/wiki/WikiManager.java +++ b/wiki/src/org/labkey/wiki/WikiManager.java @@ -1172,6 +1172,11 @@ public AttachmentParentType getAttachmentType() return WikiType.get(); } + public HtmlString getNoPermissionsMessage(User user) + { + return HtmlString.of(user.isGuest() ? "Please log in to see this data." : "You do not have permission to see this data."); + } + public static class TestCase extends Assert { WikiManager _m = null; diff --git a/wiki/src/org/labkey/wiki/WikiModule.java b/wiki/src/org/labkey/wiki/WikiModule.java index 0af37f0c893..811445d593b 100644 --- a/wiki/src/org/labkey/wiki/WikiModule.java +++ b/wiki/src/org/labkey/wiki/WikiModule.java @@ -200,7 +200,8 @@ private void loadWikiContent(@Nullable Container c, User user, String name, Stri return Set.of( WikiManager.TestCase.class, WikiController.CopyWikiContainerScopingTestCase.class, - WikiController.PermissionTestCase.class + WikiController.PermissionTestCase.class, + WikiTOC.TestCase.class ); } diff --git a/wiki/src/org/labkey/wiki/WikiTOC.java b/wiki/src/org/labkey/wiki/WikiTOC.java index b5a264c0e4f..2bfae4243ec 100644 --- a/wiki/src/org/labkey/wiki/WikiTOC.java +++ b/wiki/src/org/labkey/wiki/WikiTOC.java @@ -16,14 +16,26 @@ package org.labkey.wiki; +import jakarta.servlet.http.HttpServletResponse; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.json.JSONObject; +import org.junit.Before; +import org.junit.Test; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerManager; +import org.labkey.api.security.Group; +import org.labkey.api.security.MutableSecurityPolicy; +import org.labkey.api.security.SecurityManager; +import org.labkey.api.security.SecurityPolicyManager; import org.labkey.api.security.User; +import org.labkey.api.security.permissions.AbstractContainerScopingTest; import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.InsertPermission; +import org.labkey.api.security.permissions.ReadPermission; import org.labkey.api.security.permissions.UpdatePermission; +import org.labkey.api.security.roles.ReaderRole; +import org.labkey.api.security.roles.SubmitterRole; import org.labkey.api.util.DOM; import org.labkey.api.util.HtmlString; import org.labkey.api.util.LinkBuilder; @@ -36,8 +48,10 @@ import org.labkey.api.view.ViewContext; import org.labkey.api.view.menu.NavTreeMenu; import org.labkey.api.view.template.ClientDependency; +import org.labkey.api.wiki.WikiRendererType; import org.labkey.api.writer.HtmlWriter; import org.labkey.wiki.model.Wiki; +import org.springframework.mock.web.MockHttpServletResponse; import java.util.LinkedHashSet; import java.util.List; @@ -55,6 +69,7 @@ public class WikiTOC extends NavTreeMenu { private String _selectedLink; private final Container _cToc; + private final boolean _canRead; public WikiTOC(ViewContext context) { @@ -90,10 +105,16 @@ public WikiTOC(ViewContext context, @Nullable Portal.WebPart part) if (null == _cToc) throw new NotFoundException("Could not find container for id: \"" + id + "\""); - setId(getNavTreeId(_cToc)); - setElements(context, getNavTree()); - setCollapsible(false); - setNavMenu(createNavMenu()); + // Render the no-permission message in renderView() rather than throwing, to match wiki webpart + _canRead = _cToc.hasPermission(context.getUser(), ReadPermission.class); + + if (_canRead) + { + setId(getNavTreeId(_cToc)); + setElements(context, getNavTree()); + setCollapsible(false); + setNavMenu(createNavMenu()); + } } private NavTree createNavMenu() @@ -185,6 +206,14 @@ public LinkedHashSet getClientDependencies() protected void renderView(Object model, HtmlWriter out) { ViewContext context = getViewContext(); + User user = context.getUser(); + + // Check read permission in target container before rendering anything, GH Issue 1445 + if (!_canRead) + { + out.write(WikiManager.get().getNoPermissionsMessage(user)); + return; + } boolean isInWebPart = isInWebPart(context); @@ -316,4 +345,65 @@ private boolean isInWebPart(ViewContext context) //is page being rendered in web part or in module? return context.getActionURL().getController().equalsIgnoreCase("Project"); } + + public static class TestCase extends AbstractContainerScopingTest + { + private static final String PAGE_TITLE = "WikiTocTargetPage"; + private static final String NEW_MENU_ITEM = ">New"; + + private Container _host; + private Container _target; + + @Before + public void createFolders() + { + _host = createContainer("Host"); + _target = createContainer("Target"); + WikiManager.get().insertWiki(getAdmin(), _target, "tocPage", "body", WikiRendererType.HTML, PAGE_TITLE); + } + + @Test + public void testTocRequiresReadInTargetFolder() throws Exception + { + User user = createUserInRole(_host, ReaderRole.class); + String html = renderToc(user); + assertTrue("Expected no-permission message, html was: " + html, html.contains(WikiManager.get().getNoPermissionsMessage(user).toString())); + assertFalse("Target folder's page leaked into the TOC", html.contains(PAGE_TITLE)); + + grantRole(user, _target, ReaderRole.class); + html = renderToc(user); + assertTrue("Reader in the target folder should see its pages, html was: " + html, html.contains(PAGE_TITLE)); + + MutableSecurityPolicy policy = new MutableSecurityPolicy(_host.getPolicy()); + policy.addRoleAssignment(SecurityManager.getGroup(Group.groupGuests), ReaderRole.class); + SecurityPolicyManager.savePolicyForTests(policy, getAdmin()); + html = renderToc(User.guest); + assertTrue("Expected guest login prompt, html was: " + html, html.contains("Please log in to see this data.")); + assertFalse("Target folder's page leaked into the guest TOC", html.contains(PAGE_TITLE)); + } + + @Test + public void testTocHidesMenuWithoutReadInTargetFolder() throws Exception + { + // Submitter has Insert but not Read, so it would otherwise get the "New" menu item + User user = createUserInRole(_host, ReaderRole.class); + grantRole(user, _target, SubmitterRole.class); + String html = renderToc(user); + assertFalse("Menu should be suppressed without read, html was: " + html, html.contains(NEW_MENU_ITEM)); + + grantRole(user, _target, ReaderRole.class); + html = renderToc(user); + assertTrue("Insert + read in the target folder should show the \"New\" menu item, html was: " + html, html.contains(NEW_MENU_ITEM)); + } + + private String renderToc(User user) throws Exception + { + ActionURL url = new ActionURL("project", "getWebPart", _host) + .addParameter("webpart.name", "Wiki Table of Contents") + .addParameter("webPartContainer", _target.getId()); + MockHttpServletResponse response = get(url, user); + assertStatus(HttpServletResponse.SC_OK, response); + return new JSONObject(response.getContentAsString()).getString("html"); + } + } } diff --git a/wiki/src/org/labkey/wiki/WikiTOCFactory.java b/wiki/src/org/labkey/wiki/WikiTOCFactory.java index c8b9ef9b0d2..9035971a854 100644 --- a/wiki/src/org/labkey/wiki/WikiTOCFactory.java +++ b/wiki/src/org/labkey/wiki/WikiTOCFactory.java @@ -31,11 +31,6 @@ import java.util.HashMap; import java.util.Map; -/** - * User: adam - * Date: Nov 5, 2008 - * Time: 10:51:27 AM - */ public class WikiTOCFactory extends BaseWebPartFactory { public WikiTOCFactory() diff --git a/wiki/src/org/labkey/wiki/model/BaseWikiView.java b/wiki/src/org/labkey/wiki/model/BaseWikiView.java index 8a6b827a215..f856ada860c 100644 --- a/wiki/src/org/labkey/wiki/model/BaseWikiView.java +++ b/wiki/src/org/labkey/wiki/model/BaseWikiView.java @@ -252,7 +252,9 @@ else if (folderHasWikis) } setTitle(title); - setNavMenu(initNavMenu()); + // No nav menu if you can't read. This suppresses "New" and "Print" menu options. + if (perms.allowRead(wiki)) + setNavMenu(initNavMenu()); } diff --git a/wiki/src/org/labkey/wiki/view/wiki.jsp b/wiki/src/org/labkey/wiki/view/wiki.jsp index 8ecd8762821..421fbc66dc3 100644 --- a/wiki/src/org/labkey/wiki/view/wiki.jsp +++ b/wiki/src/org/labkey/wiki/view/wiki.jsp @@ -27,6 +27,7 @@ <%@ page import="org.labkey.wiki.WikiController" %> <%@ page import="org.labkey.wiki.model.BaseWikiView" %> <%@ page import="org.labkey.wiki.model.Wiki" %> +<%@ page import="org.labkey.wiki.WikiManager" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% @@ -44,15 +45,9 @@ if (!c.hasPermission(user, ReadPermission.class)) { - %>
<% - if (user.isGuest()) - { - %>Please log in to see this data.<% - } - else - { - %>You do not have permission to see this data.<% - }%>
<% + %> + +
<%=WikiManager.get().getNoPermissionsMessage(user)%>
<% return; } diff --git a/wiki/src/org/labkey/wiki/view/wikiVersion.jsp b/wiki/src/org/labkey/wiki/view/wikiVersion.jsp index 0ae29ab0137..4c55dc8f556 100644 --- a/wiki/src/org/labkey/wiki/view/wikiVersion.jsp +++ b/wiki/src/org/labkey/wiki/view/wikiVersion.jsp @@ -27,13 +27,13 @@ <%@ page import="org.labkey.wiki.WikiController.VersionBean" %> <%@ page import="org.labkey.wiki.WikiSelectManager" %> <%@ page import="org.labkey.wiki.model.WikiVersion" %> +<%@ page import="org.labkey.wiki.WikiManager" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <% JspView me = HttpView.currentView(); VersionBean bean = me.getModelBean(); User user = getUser(); - Container c = getContainer(); %> @@ -41,17 +41,11 @@ <% if (!bean.hasReadPermission) { - if (user.isGuest()) - { - %>Please log in to see this data.<% - } - else - { - %>You do not have permission to see this data.<% - }%> +%> + <%=WikiManager.get().getNoPermissionsMessage(user)%>
- -<%} +<% +} else { HtmlString formattedHtml = bean.html;