From 72cd0b49e1b5392c9120cdf6c3edad404c987e89 Mon Sep 17 00:00:00 2001 From: tathagta15 Date: Mon, 7 Sep 2026 10:19:34 +0530 Subject: [PATCH] [MNT-25862] Fix AccessDeniedException for generating share url (#4358) --- .../alfresco/repo/template/TemplateNode.java | 10 +- .../org/alfresco/AppContext06TestSuite.java | 1 + .../repo/template/TemplateNodeTest.java | 95 +++++++++++++++++++ 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 repository/src/test/java/org/alfresco/repo/template/TemplateNodeTest.java diff --git a/repository/src/main/java/org/alfresco/repo/template/TemplateNode.java b/repository/src/main/java/org/alfresco/repo/template/TemplateNode.java index 2a4297f632..e0ba3229ba 100644 --- a/repository/src/main/java/org/alfresco/repo/template/TemplateNode.java +++ b/repository/src/main/java/org/alfresco/repo/template/TemplateNode.java @@ -658,7 +658,14 @@ public class TemplateNode extends BasePermissionsNode implements NamespacePrefix // TODO URLs for the repo server. // TODO URLs for folders - String siteShortName = services.getSiteService().getSiteShortName(getNodeRef()); + // Run site lookup as system to avoid parent-folder permission issues. + String siteShortName = AuthenticationUtil.runAsSystem(new RunAsWork<>() { + @Override + public String doWork() throws Exception + { + return services.getSiteService().getSiteShortName(getNodeRef()); + } + }); String baseUrl = UrlUtil.getShareUrl(services.getSysAdminParams()); @@ -679,6 +686,7 @@ public class TemplateNode extends BasePermissionsNode implements NamespacePrefix // ------------------------------------------------------------------------------ // Inner classes + @Override public NamespacePrefixResolver getNamespacePrefixResolver() { return this.services.getNamespaceService(); diff --git a/repository/src/test/java/org/alfresco/AppContext06TestSuite.java b/repository/src/test/java/org/alfresco/AppContext06TestSuite.java index abde766663..374332956f 100644 --- a/repository/src/test/java/org/alfresco/AppContext06TestSuite.java +++ b/repository/src/test/java/org/alfresco/AppContext06TestSuite.java @@ -59,6 +59,7 @@ import org.alfresco.util.testing.category.NonBuildTests; org.alfresco.repo.oauth1.OAuth1CredentialsStoreServiceTest.class, org.alfresco.repo.oauth2.OAuth2CredentialsStoreServiceTest.class, org.alfresco.repo.template.TemplateServiceImplTest.class, + org.alfresco.repo.template.TemplateNodeTest.class, org.alfresco.repo.tenant.MultiTServiceImplTest.class, org.alfresco.repo.search.SearcherComponentTest.class, org.alfresco.repo.blog.BlogServiceImplTest.class, diff --git a/repository/src/test/java/org/alfresco/repo/template/TemplateNodeTest.java b/repository/src/test/java/org/alfresco/repo/template/TemplateNodeTest.java new file mode 100644 index 0000000000..943dd4d432 --- /dev/null +++ b/repository/src/test/java/org/alfresco/repo/template/TemplateNodeTest.java @@ -0,0 +1,95 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2026 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.template; + +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.springframework.transaction.annotation.Transactional; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.test_category.BaseSpringTestsCategory; +import org.alfresco.util.BaseAlfrescoSpringTest; +import org.alfresco.util.GUID; + +/** + * Test for {@link TemplateNode#getShareUrl()}. + *

+ * A user with access to a child folder only (not its parent) must still be able to resolve a share URL for a node in that child folder, e.g. from a 'Send email' rule template. + */ +@Category(BaseSpringTestsCategory.class) +@Transactional +public class TemplateNodeTest extends BaseAlfrescoSpringTest +{ + private PermissionService permissionService; + private ServiceRegistry serviceRegistry; + + @Before + public void before() throws Exception + { + super.before(); + this.permissionService = (PermissionService) this.applicationContext.getBean("permissionService"); + this.serviceRegistry = (ServiceRegistry) this.applicationContext.getBean("ServiceRegistry"); + } + + @Test + public void testGetShareUrlWhenUserCannotReadParentFolder() throws Exception + { + String userName = "templateNodeTestUser" + GUID.generate(); + createUser(userName); + + // F1: not readable by userName + NodeRef f1 = createNode(this.rootNodeRef, "f1", ContentModel.TYPE_FOLDER); + this.permissionService.setInheritParentPermissions(f1, false); + + // F2: userName only has access here, not on the parent F1 + NodeRef f2 = createNode(f1, "f2", ContentModel.TYPE_FOLDER); + this.permissionService.setInheritParentPermissions(f2, false); + this.permissionService.setPermission(f2, userName, PermissionService.CONTRIBUTOR, true); + + String shareUrl = AuthenticationUtil.runAs(() -> { + NodeRef doc = createNode(f2, "doc.txt", ContentModel.TYPE_CONTENT); + + TemplateNode templateNode = new TemplateNode(doc, this.serviceRegistry, null); + try + { + return templateNode.getShareUrl(); + } + catch (Exception e) + { + fail("getShareUrl() should not fail for a user without read access to a parent folder: " + e); + return null; + } + }, userName); + + assertNotNull(shareUrl); + assertTrue(shareUrl.contains("document-details?nodeRef=")); + } +}