diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/AceBeanInstallerClassic.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/AceBeanInstallerClassic.java index c88aebbb..259b81f3 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/AceBeanInstallerClassic.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/AceBeanInstallerClassic.java @@ -35,7 +35,10 @@ import org.apache.commons.lang3.StringUtils; import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; +import org.apache.sling.settings.SlingSettingsService; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,6 +46,7 @@ import biz.netcentric.cq.tools.actool.configmodel.AceBean; import biz.netcentric.cq.tools.actool.helper.AccessControlUtils; import biz.netcentric.cq.tools.actool.helper.RestrictionsHolder; +import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper; import biz.netcentric.cq.tools.actool.history.InstallationLogger; import biz.netcentric.cq.tools.actool.impl.SimpleNamePrincipal; @@ -53,7 +57,11 @@ public class AceBeanInstallerClassic extends BaseAceBeanInstaller implements Ace private static final Logger LOG = LoggerFactory.getLogger(AceBeanInstallerClassic.class); - + @Activate + public AceBeanInstallerClassic(@Reference SlingSettingsService slingSettingsService) { + super(RuntimeHelper.getServerType(slingSettingsService.getRunModes())); + } + /** Installs a full set of ACE beans that form an ACL for the path * * @throws RepositoryException */ diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/AceBeanInstallerIncremental.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/AceBeanInstallerIncremental.java index 346d0b5d..e62a97be 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/AceBeanInstallerIncremental.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/AceBeanInstallerIncremental.java @@ -41,6 +41,8 @@ import org.apache.commons.lang3.StringUtils; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; import org.apache.sling.jcr.api.SlingRepository; +import org.apache.sling.settings.SlingSettingsService; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferencePolicyOption; @@ -52,6 +54,8 @@ import biz.netcentric.cq.tools.actool.configmodel.Restriction; import biz.netcentric.cq.tools.actool.helper.AcHelper; import biz.netcentric.cq.tools.actool.helper.AccessControlUtils; +import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper; +import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper.ServerType; import biz.netcentric.cq.tools.actool.history.InstallationLogger; import biz.netcentric.cq.tools.actool.impl.SimpleNamePrincipal; @@ -65,6 +69,15 @@ public class AceBeanInstallerIncremental extends BaseAceBeanInstaller implements private Map> actionsToPrivilegesMapping = new ConcurrentHashMap>(); + @Activate + public AceBeanInstallerIncremental(@Reference SlingSettingsService slingSettingsService) { + this(RuntimeHelper.getServerType(slingSettingsService.getRunModes())); + } + + protected AceBeanInstallerIncremental(ServerType serverType) { + super(serverType); + } + /** Installs a full set of ACE beans that form an ACL for the path * * @throws RepositoryException */ diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/BaseAceBeanInstaller.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/BaseAceBeanInstaller.java index 6913fa3e..dc81fb4a 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/BaseAceBeanInstaller.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/BaseAceBeanInstaller.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.function.Predicate; import javax.jcr.RepositoryException; import javax.jcr.Session; @@ -42,11 +43,19 @@ import biz.netcentric.cq.tools.actool.helper.AccessControlUtils; import biz.netcentric.cq.tools.actool.helper.ContentHelper; import biz.netcentric.cq.tools.actool.helper.RestrictionsHolder; -import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper; +import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper.ServerType; import biz.netcentric.cq.tools.actool.history.InstallationLogger; /** Base Class */ public abstract class BaseAceBeanInstaller implements AceBeanInstaller { + + protected final ServerType serverType; + + private static final String[] IMMUTABLE_PATH_PREFIXES = new String[] { "/apps", "/libs" }; + + protected BaseAceBeanInstaller(ServerType serverType) { + this.serverType = serverType; + } private static final Logger LOG = LoggerFactory.getLogger(BaseAceBeanInstaller.class); @@ -65,7 +74,7 @@ public void installPathBasedACEs( history.addVerboseMessage(LOG, "Found " + paths.size() + " paths in config"); LOG.trace("Paths with ACEs: {}", paths); - paths = filterReadOnlyPaths(paths, history, session); + paths = filterForRelevantPaths(paths, history, session); // loop through all nodes from config for (final String path : paths) { @@ -107,26 +116,62 @@ public void installPathBasedACEs( + msHumanReadable(stopWatch.getTime())); } - private Set filterReadOnlyPaths(Set paths, InstallationLogger history, Session session) { - - boolean isCompositeNodeStore = RuntimeHelper.isCompositeNodeStore(session); - if (isCompositeNodeStore) { - Set pathsToKeep = new TreeSet(); - Set readOnlyPaths = new TreeSet(); - for (final String path : paths) { - if (path != null && (path.startsWith("/apps") || path.startsWith("/libs"))) { - readOnlyPaths.add(path); - } else { - pathsToKeep.add(path); - } - } - history.addMessage(LOG, "Ignoring " + readOnlyPaths.size() + " ACLs in /apps and /libs because they are ready-only (Composite NodeStore)"); + private Set filterForRelevantPaths(Set paths, InstallationLogger history, Session session) { + if (serverType == ServerType.AEM_CLOUD_RUN) { + Set pathsToKeep = removePathsWithPrefixes(paths, IMMUTABLE_PATH_PREFIXES); + history.addMessage(LOG, "Ignoring " + (paths.size() - pathsToKeep.size()) + " ACLs in " + String.join(", ", IMMUTABLE_PATH_PREFIXES) + " because they are ready-only (Composite NodeStore)"); + return pathsToKeep; + } else if (serverType == ServerType.AEM_CLOUD_IMAGE_BUILD) { + Set pathsToKeep = removePathsWithoutPrefixes(paths, IMMUTABLE_PATH_PREFIXES); + history.addMessage(LOG, "Ignoring " + (paths.size() - pathsToKeep.size()) + " ACLs outside " + String.join(", ", IMMUTABLE_PATH_PREFIXES) + " during image build, as they are overlaid"); return pathsToKeep; } else { return paths; } } + static final class PrefixesFilter implements java.util.function.Predicate { + private final String[] prefixes; + + public PrefixesFilter(String... prefixes) { + // the prefixes should be normalized (e.g. no trailing slash) to avoid issues with matching, but we do not want to enforce this on the caller, so we do it here + Arrays.stream(prefixes).forEach(prefix -> { + if (prefix != null && prefix.endsWith("/") && prefix.length() > 1) { + throw new IllegalArgumentException("Prefixes must not end with a slash, but given prefix \"" + prefix + "\" ends with a slash"); + } + }); + this.prefixes = prefixes; + } + + @Override + public boolean test(String path) { + for (String prefix : prefixes) { + if (path != null && (path.equals(prefix) || path.startsWith(prefix + "/"))) { + return true; + } + } + return false; + } + } + + static Set removePathsWithPrefixes(Set paths, String... prefixes) { + return removePathWithPredicate(paths, new PrefixesFilter(prefixes)); + } + + static Set removePathsWithoutPrefixes(Set paths, String... prefixes) { + return removePathWithPredicate(paths, Predicate.not(new PrefixesFilter(prefixes))); + } + + static Set removePathWithPredicate(Set paths, Predicate pathPredicate) { + Set filteredPaths = new TreeSet<>(); + for (String path : paths) { + if (!pathPredicate.test(path)) { + filteredPaths.add(path); + } + } + return filteredPaths; + } + /** Installs a full set of ACE beans that form an ACL for the path * * @throws RepositoryException */ diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/runtime/RuntimeHelper.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/runtime/RuntimeHelper.java index 76d2d6c6..b70edc53 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/runtime/RuntimeHelper.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/runtime/RuntimeHelper.java @@ -1,5 +1,8 @@ package biz.netcentric.cq.tools.actool.helper.runtime; +import java.util.Collection; +import java.util.Objects; + /*- * #%L * Access Control Tool Bundle @@ -25,12 +28,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** Provides basic context information about the runtime. In own package so it can be inlined with "Conditional-Package" from startup bundle */ +/** Provides basic context information about the runtime. + * Located in its own package so it can be inlined with "Conditional-Package" from startup bundle */ public class RuntimeHelper { public static final Logger LOG = LoggerFactory.getLogger(RuntimeHelper.class); private static final String INSTALLER_CORE_BUNDLE_SYMBOLIC_ID = "org.apache.sling.installer.core"; + /** + * + * @param session + * @return + * @deprecated Use {@link #getServerType(Collection)} instead + */ public static boolean isCompositeNodeStore(Session session) { try { @@ -52,7 +62,7 @@ public static boolean isCompositeNodeStore(Session session) { throw new IllegalStateException("Could not check if session is connected to a composite node store: "+e, e); } } - + public static int getCurrentStartLevel() { return getCurrentStartLevel(FrameworkUtil.getBundle(RuntimeHelper.class).getBundleContext()); } @@ -61,8 +71,13 @@ public static int getCurrentStartLevel(BundleContext bundleContext) { return bundleContext.getBundle(Constants.SYSTEM_BUNDLE_ID).adapt(FrameworkStartLevel.class).getStartLevel(); } + /** + * + * @return {@code true} if the installer core bundle is not present (the case for all AEMaaCS runtimes running in the cloud) + * @deprecated Use {@link #getServerType(Collection)} instead + */ + @Deprecated public static boolean isCloudReadyInstance() { - boolean isCloudReadyInstance = true; Bundle[] bundles = FrameworkUtil.getBundle(RuntimeHelper.class).getBundleContext().getBundles(); for (Bundle bundle : bundles) { @@ -74,4 +89,67 @@ public static boolean isCloudReadyInstance() { return isCloudReadyInstance; } + /** + * The server type encapsulates information about the runtime environment, + * especially regarding the type of AEM instance (classic vs cloud) and whether it is running in a composite node store or not. + */ + public enum ServerType { + /** AEM 6.5 (LTS) and older */ + AEM_CLASSIC(false, false), + /** AEM as a Cloud Service SDK (probably running locally) */ + AEM_CLOUD_SDK(true, false), + /** AEM as a Cloud Service during Cloud Manager image build (with composite node store seed mode) */ + AEM_CLOUD_IMAGE_BUILD(true, true), + /** AEM as a Cloud Service running in Adobe Cloud (with composite node store) */ + AEM_CLOUD_RUN(true, true), + /** Any other (non AEM) Sling based runtime */ + SLING(false, false); + + private final boolean isAEMaaCS; + private final boolean isInCloud; + + ServerType(boolean isAEMaaCS, boolean isInCloud) { + this.isAEMaaCS = isAEMaaCS; + this.isInCloud = isInCloud; + } + + /** + * Indicates whether this server type is an AEM as a Cloud Service runtime (either SDK, image build or cloud run). + */ + public boolean isAEMaaCS() { + return isAEMaaCS; + } + + /** + * Indicates whether this server type is running in the cloud with composite node store (either image build or cloud run). + */ + public boolean isInCloud() { + return isInCloud; + } + } + + /** + * Determines the server type based on the provided run modes. + * @param runModes The collection of active run modes. + * @return The determined {@link ServerType}. + */ + public static ServerType getServerType(Collection runModes) { + Objects.requireNonNull(runModes, "Run modes collection cannot be null"); + // "crx3" is available on all AEMs + // "crx3composite-seed" is only available on composite node store seed mode (which is currently only used for cloud image build) + // "sdk" (next to "crx3, crx3tarmk, crx3tar) is only available on cloud sdk + // "crx3composite" and "cloud-ready" in cloud run instances + // everything else would be Sling + if(runModes.contains("sdk")) { + return ServerType.AEM_CLOUD_SDK; + } else if(runModes.contains("crx3composite-seed")) { + return ServerType.AEM_CLOUD_IMAGE_BUILD; + } else if(runModes.contains("crx3composite")) { + return ServerType.AEM_CLOUD_RUN; + } else if(runModes.contains("crx3")) { + return ServerType.AEM_CLASSIC; + } else { + return ServerType.SLING; + } + } } diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/impl/AcHistoryServiceImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/impl/AcHistoryServiceImpl.java index b9a8df00..3495c022 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/impl/AcHistoryServiceImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/impl/AcHistoryServiceImpl.java @@ -28,6 +28,7 @@ import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.commons.JcrUtils; import org.apache.sling.jcr.api.SlingRepository; +import org.apache.sling.settings.SlingSettingsService; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; @@ -38,6 +39,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper; import biz.netcentric.cq.tools.actool.history.AcHistoryService; import biz.netcentric.cq.tools.actool.history.AcToolExecution; import biz.netcentric.cq.tools.actool.history.impl.AcHistoryServiceImpl.Configuration; @@ -57,6 +59,9 @@ public class AcHistoryServiceImpl implements AcHistoryService { @Reference(policyOption = ReferencePolicyOption.GREEDY) private SlingRepository repository; + @Reference(policyOption = ReferencePolicyOption.GREEDY) + private SlingSettingsService slingSettingsService; + @ObjectClassDefinition(name = "AC Tool History Service", description="Service that writes & fetches Ac installation histories.", id="biz.netcentric.cq.tools.actool.history.impl.AcHistoryServiceImpl") @@ -83,7 +88,7 @@ public void persistHistory(PersistableInstallationLogger installLog) { try { session = repository.loginService(null, null); - Node historyNode = HistoryUtils.persistHistory(session, installLog, nrOfSavedHistories); + Node historyNode = HistoryUtils.persistHistory(session, installLog, nrOfSavedHistories, RuntimeHelper.getServerType(slingSettingsService.getRunModes())); String mergedAndProcessedConfig = installLog.getMergedAndProcessedConfig(); if (StringUtils.isNotBlank(mergedAndProcessedConfig)) { diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/impl/HistoryUtils.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/impl/HistoryUtils.java index ddf096ce..7a7adf69 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/impl/HistoryUtils.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/impl/HistoryUtils.java @@ -48,6 +48,7 @@ import biz.netcentric.cq.tools.actool.api.InstallationResult; import biz.netcentric.cq.tools.actool.comparators.TimestampPropertyComparator; import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper; +import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper.ServerType; import biz.netcentric.cq.tools.actool.history.AcToolExecution; import biz.netcentric.cq.tools.actool.jmx.AceServiceMBeanImpl; import biz.netcentric.cq.tools.actool.ui.AcToolWebconsolePlugin; @@ -102,10 +103,11 @@ public static Node getAcHistoryRootNode(final Session session) * @param nrOfHistoriesToSave * number of newest histories which should be kept in CRX. older * histories get automatically deleted + * @param serverType * @return the node being created */ public static Node persistHistory(final Session session, - PersistableInstallationLogger installLog, final int nrOfHistoriesToSave) + PersistableInstallationLogger installLog, final int nrOfHistoriesToSave, ServerType serverType) throws RepositoryException { Node acHistoryRootNode = getAcHistoryRootNode(session); @@ -126,7 +128,7 @@ public static Node persistHistory(final Session session, trigger = "startup_hook_pckmgr)"; } else { // if the history is not yet copied to apps, it's the image build - boolean isImageBuild = RuntimeHelper.isCloudReadyInstance() && !session.itemExists(AC_HISTORY_PATH_IN_APPS); + boolean isImageBuild = serverType == ServerType.AEM_CLOUD_IMAGE_BUILD && !session.itemExists(AC_HISTORY_PATH_IN_APPS); if(isImageBuild) { trigger = "startup_hook_image_build"; } else { diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcConfigChangeTracker.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcConfigChangeTracker.java index 8163da6f..854e4e77 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcConfigChangeTracker.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcConfigChangeTracker.java @@ -22,12 +22,16 @@ import javax.jcr.Session; import org.apache.commons.lang3.StringUtils; +import org.apache.sling.settings.SlingSettingsService; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import biz.netcentric.cq.tools.actool.api.InstallationOptions; import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper; +import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper.ServerType; import biz.netcentric.cq.tools.actool.history.impl.HistoryUtils; /** @@ -36,6 +40,13 @@ @Component(service=AcConfigChangeTracker.class) public class AcConfigChangeTracker { private static final Logger LOG = LoggerFactory.getLogger(AcConfigChangeTracker.class); + + private final ServerType serverType; + + @Activate + public AcConfigChangeTracker(@Reference SlingSettingsService slingSettingsService) { + this.serverType = RuntimeHelper.getServerType(slingSettingsService.getRunModes()); + } public boolean configIsUnchangedComparedToLastExecution(Map configFiles, Session session, InstallationOptions options) { @@ -68,7 +79,7 @@ public boolean configIsUnchangedComparedToLastExecution(Map conf } private String createExecutionKey(Map configFiles, Session session, InstallationOptions options) { - boolean isCompositeNodeStore= RuntimeHelper.isCompositeNodeStore(session); + boolean isCompositeNodeStore = serverType == ServerType.AEM_CLOUD_RUN; String restrictedToPathsKey = options.getRestrictedToPaths().isEmpty() ? "ALL_PATHS" : String.join("+", options.getRestrictedToPaths()).replace("$", "").replace("^", ""); String effectiveRootPathOfConfigs = getEffectiveConfigRootPath(configFiles); String executionKey = "hash("+StringUtils.removeEnd(effectiveRootPathOfConfigs, "/").replace('/', '\\') + "," + restrictedToPathsKey.replace('/', '\\').replace(':', '_')+","+(isCompositeNodeStore?"compNodeStore":"stdRepo")+")"; diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHook.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHook.java index cfae5d67..f3062130 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHook.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHook.java @@ -41,7 +41,6 @@ public void execute(InstallContext context) throws PackageException { switch (context.getPhase()) { case PREPARE: if (!shouldInstallInPhaseInstalled(context.getPackage())) { - install(context); } break; @@ -73,7 +72,7 @@ private void install(InstallContext context) throws PackageException { } alreadyRan = true; - if (RuntimeHelper.isCloudReadyInstance()) { + if (getServerType().isInCloud()) { log("InstallHook is skipped by default in cloud (use package property 'actool.forceInstallHookInCloud = true' to force run)", listener); return; diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/OsgiAwareInstallHook.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/OsgiAwareInstallHook.java index e970129f..ac9b84ba 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/OsgiAwareInstallHook.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/OsgiAwareInstallHook.java @@ -16,6 +16,7 @@ import org.apache.jackrabbit.vault.fs.api.ProgressTrackerListener; import org.apache.jackrabbit.vault.packaging.InstallHook; +import org.apache.sling.settings.SlingSettingsService; import org.osgi.annotation.versioning.ProviderType; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; @@ -24,6 +25,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper; +import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper.ServerType; + /** * A Jackrabbit FileVault install hook @@ -39,6 +43,7 @@ public abstract class OsgiAwareInstallHook implements InstallHook { private final BundleContext bundleContext; private static final Logger LOG = LoggerFactory.getLogger(OsgiAwareInstallHook.class); + private ServerType serverType; public OsgiAwareInstallHook() throws ClassCastException { // since this class was loaded through a bundle class loader as well, just take the bundle context @@ -68,4 +73,18 @@ protected void log(String message, ProgressTrackerListener listener) { LOG.info(message); } } + + public ServerType getServerType() { + if (serverType == null) { + ServiceReference slingSettingsReference = getServiceReference(SlingSettingsService.class); + try { + SlingSettingsService slingSettings = getBundleContext().getService(slingSettingsReference); + serverType = RuntimeHelper.getServerType(slingSettings.getRunModes()); + } + finally { + getBundleContext().ungetService(slingSettingsReference); + } + } + return serverType; + } } diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/package-info.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/package-info.java index 6cb8b9f4..7b5789f4 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/package-info.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/package-info.java @@ -1,5 +1,5 @@ -@Version("3.0.0") +@Version("3.1.0") package biz.netcentric.cq.tools.actool.installhook; /*- diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/slingsettings/ExtendedSlingSettingsService.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/slingsettings/ExtendedSlingSettingsService.java index ea3a23d3..18f94df4 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/slingsettings/ExtendedSlingSettingsService.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/slingsettings/ExtendedSlingSettingsService.java @@ -1,11 +1,3 @@ -/* - * (C) Copyright 2020 Netcentric, A Cognizant Digital Business. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html - */ package biz.netcentric.cq.tools.actool.slingsettings; /*- diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/slingsettings/ExtendedSlingSettingsServiceImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/slingsettings/ExtendedSlingSettingsServiceImpl.java index e232440e..95679f65 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/slingsettings/ExtendedSlingSettingsServiceImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/slingsettings/ExtendedSlingSettingsServiceImpl.java @@ -1,11 +1,3 @@ -/* - * (C) Copyright 2020 Netcentric, A Cognizant Digital Business. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html - */ package biz.netcentric.cq.tools.actool.slingsettings; /*- @@ -81,8 +73,8 @@ public void activate(Config config) { Set defaultRunmodes = slingSettingsService.getRunModes(); extendedRunmodes = new HashSet<>(); extendedRunmodes.addAll(defaultRunmodes); - boolean isCloudReady = RuntimeHelper.isCloudReadyInstance(); - if(isCloudReady) { + boolean isInCloud = RuntimeHelper.getServerType(defaultRunmodes).isInCloud(); + if (isInCloud) { extendedRunmodes.add(ADDITIONAL_RUNMODE_CLOUD); } @@ -91,7 +83,7 @@ public void activate(Config config) { extendedRunmodes.addAll(additionalRunmodes); } - LOG.info("Default runmodes: {} Extended Runmodes: {} isCloudReady: {}", defaultRunmodes, extendedRunmodes, isCloudReady); + LOG.info("Default runmodes: {}; Extended Runmodes: {}; isInCloud: {}", defaultRunmodes, extendedRunmodes, isInCloud); } @Override diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceinstaller/AceBeanInstallerIncrementalTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceinstaller/AceBeanInstallerIncrementalTest.java index b2da5e7c..f977dc89 100644 --- a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceinstaller/AceBeanInstallerIncrementalTest.java +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceinstaller/AceBeanInstallerIncrementalTest.java @@ -69,6 +69,7 @@ import biz.netcentric.cq.tools.actool.configmodel.AceBean; import biz.netcentric.cq.tools.actool.configmodel.Restriction; import biz.netcentric.cq.tools.actool.configreader.YamlConfigReader; +import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper.ServerType; import biz.netcentric.cq.tools.actool.history.InstallationLogger; @ExtendWith(MockitoExtension.class) @@ -94,7 +95,7 @@ public class AceBeanInstallerIncrementalTest { @Spy @InjectMocks - AceBeanInstallerIncremental aceBeanInstallerIncremental; + AceBeanInstallerIncremental aceBeanInstallerIncremental = new AceBeanInstallerIncremental(ServerType.AEM_CLASSIC); @Spy InstallationLogger installLog; @@ -113,7 +114,6 @@ public class AceBeanInstallerIncrementalTest { @BeforeEach public void setup() throws RepositoryException { - doReturn(accessControlManager).when(session).getAccessControlManager(); // empty by default diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceinstaller/BaseAceBeanInstallerTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceinstaller/BaseAceBeanInstallerTest.java new file mode 100644 index 00000000..d1c7cebc --- /dev/null +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceinstaller/BaseAceBeanInstallerTest.java @@ -0,0 +1,34 @@ +package biz.netcentric.cq.tools.actool.aceinstaller; + +/*- + * #%L + * Access Control Tool Bundle + * %% + * Copyright (C) 2015 - 2025 Cognizant Netcentric + * %% + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * #L% + */ + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Set; + +import org.junit.jupiter.api.Test; + +class BaseAceBeanInstallerTest { + + @Test + void removePathsWithPrefixes() { + assertEquals(Set.of("/apps/test", "/content2/other"), BaseAceBeanInstaller.removePathsWithPrefixes(Set.of("/content/test", "/content/test2", "/apps/test", "/content2/other"), new String[] { "/content" })); + } + + @Test + void removePathsWithoutPrefixes() { + assertEquals(Set.of("/content/test", "/content/test2"), BaseAceBeanInstaller.removePathsWithoutPrefixes(Set.of("/content/test", "/content/test2", "/apps/test", "/content2/other"), new String[] { "/content" })); + } +} diff --git a/accesscontroltool-startuphook-bundle/src/main/java/biz/netcentric/cq/tools/actool/startuphook/impl/AcToolStartupHookServiceImpl.java b/accesscontroltool-startuphook-bundle/src/main/java/biz/netcentric/cq/tools/actool/startuphook/impl/AcToolStartupHookServiceImpl.java index 7cc94dbc..01c15837 100644 --- a/accesscontroltool-startuphook-bundle/src/main/java/biz/netcentric/cq/tools/actool/startuphook/impl/AcToolStartupHookServiceImpl.java +++ b/accesscontroltool-startuphook-bundle/src/main/java/biz/netcentric/cq/tools/actool/startuphook/impl/AcToolStartupHookServiceImpl.java @@ -27,6 +27,7 @@ import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants; import org.apache.sling.jcr.api.SlingRepository; +import org.apache.sling.settings.SlingSettingsService; import org.osgi.framework.BundleContext; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -40,6 +41,7 @@ import biz.netcentric.cq.tools.actool.api.AcInstallationService; import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper; +import biz.netcentric.cq.tools.actool.helper.runtime.RuntimeHelper.ServerType; import biz.netcentric.cq.tools.actool.history.impl.HistoryUtils; @Component @@ -67,13 +69,16 @@ public enum StartupHookActivation { @Reference(policyOption = ReferencePolicyOption.GREEDY) private SlingRepository repository; + + @Reference(policyOption = ReferencePolicyOption.GREEDY) + private SlingSettingsService slingSettingsService; - private boolean isCompositeNodeStore; + private ServerType serverType; @Activate public void activate(BundleContext bundleContext, Config config) { - - boolean isCloudReady = RuntimeHelper.isCloudReadyInstance(); + this.serverType = RuntimeHelper.getServerType(slingSettingsService.getRunModes()); + boolean isCloudReady = serverType.isInCloud(); Config.StartupHookActivation activationMode = config.activationMode(); boolean runAsyncForMutableConent = config.runAsyncForMutableConent(); int currentStartLevel = RuntimeHelper.getCurrentStartLevel(bundleContext); @@ -92,7 +97,7 @@ public void activate(BundleContext bundleContext, Config config) { LOG.info("Running AcTool with " + (relevantPathsForInstallation.isEmpty() ? "all paths" : "paths " + relevantPathsForInstallation) + "..."); - if (runAsyncForMutableConent && isCompositeNodeStore) { + if (runAsyncForMutableConent && serverType == ServerType.AEM_CLOUD_RUN) { LOG.info( "Running AcTool asynchronously on mutable content of composite node store (config runAsyncForMutableConent=true)..."); runAcToolAsync(relevantPathsForInstallation, currentStartLevel, isCloudReady); @@ -132,10 +137,9 @@ private List getRelevantPathsForInstallation() { try { session = repository.loginService(null, null); - isCompositeNodeStore = RuntimeHelper.isCompositeNodeStore(session); - LOG.info("Repo is running with Composite NodeStore: {}", isCompositeNodeStore); + LOG.info("Repo is running with server type: {}", serverType); - if(!isCompositeNodeStore) { + if(serverType != ServerType.AEM_CLOUD_RUN) { return Collections.emptyList(); } @@ -150,7 +154,7 @@ private List getRelevantPathsForInstallation() { AccessControlConstants.REP_REPO_POLICY).contains(node.getName())) { continue; } - if (isCompositeNodeStore && Arrays.asList("apps", "libs").contains(node.getName())) { + if (serverType == ServerType.AEM_CLOUD_RUN && Arrays.asList("apps", "libs").contains(node.getName())) { continue; } relevantPathsForInstallation.add(node.getPath()); @@ -180,7 +184,7 @@ private void copyAcHistoryToOrFromApps(boolean isCloudReady) { try { session = repository.loginService(null, null); - if(isCompositeNodeStore) { + if(serverType == ServerType.AEM_CLOUD_RUN) { LOG.info("Restoring history from /apps to /var"); if(session.nodeExists(HistoryUtils.AC_HISTORY_PATH_IN_APPS)) {