From d70fb8363b6d8832823c1638f2643aec9a6894e5 Mon Sep 17 00:00:00 2001 From: Krystian Panek Date: Fri, 16 May 2025 15:14:48 +0200 Subject: [PATCH 1/2] Dedicated service resource resolvers --- .../es/aem/acm/core/code/ExecutionQueue.java | 2 +- .../vml/es/aem/acm/core/code/Executor.java | 2 +- .../aem/acm/core/instance/HealthChecker.java | 2 +- .../es/aem/acm/core/mock/MockHttpFilter.java | 2 +- .../aem/acm/core/script/ScriptScheduler.java | 2 +- .../es/aem/acm/core/util/ResourceUtils.java | 50 +++++++++++++------ ui.config/pom.xml | 2 + ....repoinit.RepositoryInitializer~acm.config | 10 ++-- 8 files changed, 50 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/com/vml/es/aem/acm/core/code/ExecutionQueue.java b/core/src/main/java/com/vml/es/aem/acm/core/code/ExecutionQueue.java index 69d7f46d..9dfb428b 100644 --- a/core/src/main/java/com/vml/es/aem/acm/core/code/ExecutionQueue.java +++ b/core/src/main/java/com/vml/es/aem/acm/core/code/ExecutionQueue.java @@ -182,7 +182,7 @@ public JobExecutionResult process(Job job, JobExecutionContext context) { private Execution executeAsync(ExecutionContextOptions contextOptions, QueuedExecution execution) throws AcmException { try (ResourceResolver resolver = - ResourceUtils.serviceResolver(resourceResolverFactory, contextOptions.getUserId()); + ResourceUtils.contentResolver(resourceResolverFactory, contextOptions.getUserId()); ExecutionContext context = executor.createContext( execution.getJob().getId(), contextOptions.getExecutionMode(), diff --git a/core/src/main/java/com/vml/es/aem/acm/core/code/Executor.java b/core/src/main/java/com/vml/es/aem/acm/core/code/Executor.java index d0ff897a..8003a888 100644 --- a/core/src/main/java/com/vml/es/aem/acm/core/code/Executor.java +++ b/core/src/main/java/com/vml/es/aem/acm/core/code/Executor.java @@ -59,7 +59,7 @@ public ExecutionContext createContext( public Execution execute(Executable executable, ExecutionContextOptions contextOptions) throws AcmException { try (ResourceResolver resourceResolver = - ResourceUtils.serviceResolver(resourceResolverFactory, contextOptions.getUserId()); + ResourceUtils.contentResolver(resourceResolverFactory, contextOptions.getUserId()); ExecutionContext executionContext = createContext( ExecutionId.generate(), contextOptions.getExecutionMode(), executable, resourceResolver)) { return execute(executionContext); diff --git a/core/src/main/java/com/vml/es/aem/acm/core/instance/HealthChecker.java b/core/src/main/java/com/vml/es/aem/acm/core/instance/HealthChecker.java index 7b1ae898..5d690ae4 100644 --- a/core/src/main/java/com/vml/es/aem/acm/core/instance/HealthChecker.java +++ b/core/src/main/java/com/vml/es/aem/acm/core/instance/HealthChecker.java @@ -63,7 +63,7 @@ protected void deactivate() { } public HealthStatus checkStatus() { - try (ResourceResolver resourceResolver = ResourceUtils.serviceResolver(resourceResolverFactory, null)) { + try (ResourceResolver resourceResolver = ResourceUtils.contentResolver(resourceResolverFactory, null)) { return checkStatus(resourceResolver); } catch (Exception e) { LOG.error("Health checker failed", e); diff --git a/core/src/main/java/com/vml/es/aem/acm/core/mock/MockHttpFilter.java b/core/src/main/java/com/vml/es/aem/acm/core/mock/MockHttpFilter.java index 328abaae..a25dd52a 100644 --- a/core/src/main/java/com/vml/es/aem/acm/core/mock/MockHttpFilter.java +++ b/core/src/main/java/com/vml/es/aem/acm/core/mock/MockHttpFilter.java @@ -61,7 +61,7 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; - try (ResourceResolver resolver = ResourceUtils.serviceResolver(resolverFactory, null)) { + try (ResourceResolver resolver = ResourceUtils.mockResolver(resolverFactory)) { CodeContext codeContext = new CodeContext(osgiContext, resolver); MockRepository repository = new MockRepository(resolver); diff --git a/core/src/main/java/com/vml/es/aem/acm/core/script/ScriptScheduler.java b/core/src/main/java/com/vml/es/aem/acm/core/script/ScriptScheduler.java index d0ea42a9..41dd9471 100644 --- a/core/src/main/java/com/vml/es/aem/acm/core/script/ScriptScheduler.java +++ b/core/src/main/java/com/vml/es/aem/acm/core/script/ScriptScheduler.java @@ -101,7 +101,7 @@ public void run() { ExecutionContextOptions contextOptions = new ExecutionContextOptions(ExecutionMode.RUN, config.userImpersonationId()); try (ResourceResolver resourceResolver = - ResourceUtils.serviceResolver(resourceResolverFactory, contextOptions.getUserId())) { + ResourceUtils.contentResolver(resourceResolverFactory, contextOptions.getUserId())) { ScriptRepository scriptRepository = new ScriptRepository(resourceResolver); scriptRepository.clean(); diff --git a/core/src/main/java/com/vml/es/aem/acm/core/util/ResourceUtils.java b/core/src/main/java/com/vml/es/aem/acm/core/util/ResourceUtils.java index 73ce9635..bb40c44f 100644 --- a/core/src/main/java/com/vml/es/aem/acm/core/util/ResourceUtils.java +++ b/core/src/main/java/com/vml/es/aem/acm/core/util/ResourceUtils.java @@ -13,32 +13,54 @@ public final class ResourceUtils { + public enum Subservice { + CONTENT("acm-content-service"), + MOCK("acm-mock-service"); + + public final String userId; + + Subservice(String userId) { + this.userId = userId; + } + } + private ResourceUtils() { // intentionally empty } - public static ResourceResolver serviceResolver( + public static ResourceResolver contentResolver( ResourceResolverFactory resourceResolverFactory, String userImpersonationId) throws LoginException { - boolean impersonation = StringUtils.isNotBlank(userImpersonationId); + return serviceResolver(resourceResolverFactory, Subservice.CONTENT.userId, userImpersonationId); + } + + public static ResourceResolver mockResolver(ResourceResolverFactory resourceResolverFactory) throws LoginException { + return serviceResolver(resourceResolverFactory, Subservice.MOCK.userId, null); + } + + private static ResourceResolver serviceResolver( + ResourceResolverFactory resourceResolverFactory, String subservice, String userImpersonationId) + throws LoginException { Map params = new HashMap<>(); - params.put(ResourceResolverFactory.SUBSERVICE, "acm"); - if (impersonation) { - params.put(ResourceResolverFactory.USER_IMPERSONATION, userImpersonationId); + params.put(ResourceResolverFactory.SUBSERVICE, subservice); + + boolean impersonation = StringUtils.isNotBlank(userImpersonationId); + if (!impersonation) { + return resourceResolverFactory.getServiceResourceResolver(params); } + try { + params.put(ResourceResolverFactory.USER_IMPERSONATION, userImpersonationId); ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(params); - if (impersonation) { - String userImpersonationIdEffective = serviceOrImpersonatedUserId(resolver); - if (!StringUtils.equals(userImpersonationId, userImpersonationIdEffective)) { - throw new AcmException(String.format( - "Cannot impersonate user '%s' as service user '%s' is used instead!", - serviceOrImpersonatedUserId(resolver), userImpersonationId)); - } + String userImpersonationIdEffective = serviceOrImpersonatedUserId(resolver); + if (!StringUtils.equals(userImpersonationId, userImpersonationIdEffective)) { + throw new AcmException(String.format( + "Cannot impersonate user '%s' as service user '%s' is used instead!", + serviceOrImpersonatedUserId(resolver), userImpersonationId)); } return resolver; } catch (LoginException e) { - return resourceResolverFactory.getAdministrativeResourceResolver( - params); // fix for 'Impersonation not allowed' on 6.5.0 (supported by login admin whitelist) + // fix for 'Impersonation not allowed' on 6.5.0 (supported by login admin whitelist) + return resourceResolverFactory.getAdministrativeResourceResolver(params); } } diff --git a/ui.config/pom.xml b/ui.config/pom.xml index bf4ace1d..b1bee632 100644 --- a/ui.config/pom.xml +++ b/ui.config/pom.xml @@ -49,6 +49,8 @@ none + com.vml.es.aem.acm + acm.ui.config container false diff --git a/ui.config/src/main/content/jcr_root/apps/acm-config/osgiconfig/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~acm.config b/ui.config/src/main/content/jcr_root/apps/acm-config/osgiconfig/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~acm.config index 8315ebbc..06ae7a7e 100644 --- a/ui.config/src/main/content/jcr_root/apps/acm-config/osgiconfig/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~acm.config +++ b/ui.config/src/main/content/jcr_root/apps/acm-config/osgiconfig/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~acm.config @@ -8,9 +8,13 @@ scripts=[" create path /conf/acm/settings/script/auto/enabled(sling:OrderedFolder) create path /var/acm(sling:OrderedFolder) - create service user acm-service with path system/acm/acm-service - - set ACL for acm-service + create service user acm-content-service with path system/acm/acm-content-service + set ACL for acm-content-service allow jcr:all on / end + + create service user acm-mock-service with path system/acm/acm-mock-service + set ACL for acm-mock-service + allow jcr:read on /content + end "] From 8c39851c1e55476e3b4676798e41488009466fd3 Mon Sep 17 00:00:00 2001 From: Krystian Panek Date: Fri, 16 May 2025 15:36:22 +0200 Subject: [PATCH 2/2] Mock perms related hardening --- Taskfile.yml | 1 + .../java/com/vml/es/aem/acm/core/acl/AclContext.java | 5 +---- .../com/vml/es/aem/acm/core/util/ResourceUtils.java | 12 ++++++++---- ...ing.jcr.repoinit.RepositoryInitializer~acm.config | 1 + ...ing.impl.ServiceUserMapperImpl.amended-acm.config | 3 ++- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index e8d3a951..ed11f987 100755 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -180,6 +180,7 @@ tasks: - src/**/* - ../ui.apps/target/*.ui.apps-*.zip - ../ui.content/target/*.ui.content-*.zip + - ../ui.config/target/*.ui.config-*.zip - ../ui.content.migration/target/*.ui.content.migration-*.zip - ../ui.content.permissions/target/*.ui.content.permissions-*.zip generates: [ target/*.all-*.zip ] diff --git a/core/src/main/java/com/vml/es/aem/acm/core/acl/AclContext.java b/core/src/main/java/com/vml/es/aem/acm/core/acl/AclContext.java index 112b018e..27c143dd 100644 --- a/core/src/main/java/com/vml/es/aem/acm/core/acl/AclContext.java +++ b/core/src/main/java/com/vml/es/aem/acm/core/acl/AclContext.java @@ -30,8 +30,6 @@ public class AclContext { private final PermissionsManager permissionsManager; - private final boolean compositeNodeStore; - public AclContext(ResourceResolver resourceResolver) { try { this.logger = LoggerFactory.getLogger(AclContext.class); @@ -42,7 +40,6 @@ public AclContext(ResourceResolver resourceResolver) { this.resourceResolver = resourceResolver; this.authorizableManager = new AuthorizableManager(session, userManager, valueFactory); this.permissionsManager = new PermissionsManager(session, accessControlManager, valueFactory); - this.compositeNodeStore = new Repo(resourceResolver).isCompositeNodeStore(); } catch (RepositoryException e) { throw new AclException("Cannot access repository while obtaining ACL context!", e); } @@ -65,7 +62,7 @@ public PermissionsManager getPermissionsManager() { } public boolean isCompositeNodeStore() { - return compositeNodeStore; + return new Repo(resourceResolver).isCompositeNodeStore(); } public AclUser determineUser(User user) { diff --git a/core/src/main/java/com/vml/es/aem/acm/core/util/ResourceUtils.java b/core/src/main/java/com/vml/es/aem/acm/core/util/ResourceUtils.java index bb40c44f..6a2a26ff 100644 --- a/core/src/main/java/com/vml/es/aem/acm/core/util/ResourceUtils.java +++ b/core/src/main/java/com/vml/es/aem/acm/core/util/ResourceUtils.java @@ -22,6 +22,10 @@ public enum Subservice { Subservice(String userId) { this.userId = userId; } + + public String id() { + return name().toLowerCase(); + } } private ResourceUtils() { @@ -30,18 +34,18 @@ private ResourceUtils() { public static ResourceResolver contentResolver( ResourceResolverFactory resourceResolverFactory, String userImpersonationId) throws LoginException { - return serviceResolver(resourceResolverFactory, Subservice.CONTENT.userId, userImpersonationId); + return serviceResolver(resourceResolverFactory, Subservice.CONTENT, userImpersonationId); } public static ResourceResolver mockResolver(ResourceResolverFactory resourceResolverFactory) throws LoginException { - return serviceResolver(resourceResolverFactory, Subservice.MOCK.userId, null); + return serviceResolver(resourceResolverFactory, Subservice.MOCK, null); } private static ResourceResolver serviceResolver( - ResourceResolverFactory resourceResolverFactory, String subservice, String userImpersonationId) + ResourceResolverFactory resourceResolverFactory, Subservice subservice, String userImpersonationId) throws LoginException { Map params = new HashMap<>(); - params.put(ResourceResolverFactory.SUBSERVICE, subservice); + params.put(ResourceResolverFactory.SUBSERVICE, subservice.id()); boolean impersonation = StringUtils.isNotBlank(userImpersonationId); if (!impersonation) { diff --git a/ui.config/src/main/content/jcr_root/apps/acm-config/osgiconfig/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~acm.config b/ui.config/src/main/content/jcr_root/apps/acm-config/osgiconfig/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~acm.config index 06ae7a7e..dde6e30c 100644 --- a/ui.config/src/main/content/jcr_root/apps/acm-config/osgiconfig/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~acm.config +++ b/ui.config/src/main/content/jcr_root/apps/acm-config/osgiconfig/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~acm.config @@ -16,5 +16,6 @@ scripts=[" create service user acm-mock-service with path system/acm/acm-mock-service set ACL for acm-mock-service allow jcr:read on /content + allow jcr:read on /conf end "] diff --git a/ui.config/src/main/content/jcr_root/apps/acm-config/osgiconfig/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-acm.config b/ui.config/src/main/content/jcr_root/apps/acm-config/osgiconfig/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-acm.config index 93f63107..aac6ccd2 100644 --- a/ui.config/src/main/content/jcr_root/apps/acm-config/osgiconfig/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-acm.config +++ b/ui.config/src/main/content/jcr_root/apps/acm-config/osgiconfig/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-acm.config @@ -1,2 +1,3 @@ service.ranking=I"100" -user.mapping=["acm.core:acm\=acm-service"] +user.mapping=["acm.core:content\=acm-content-service", "acm.core:mock\=acm-mock-service"] +