Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,18 @@
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;

import biz.netcentric.cq.tools.actool.aem.AcToolCqActions;
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;

Expand All @@ -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 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -65,6 +69,15 @@ public class AceBeanInstallerIncremental extends BaseAceBeanInstaller implements

private Map<String, Set<AceBean>> actionsToPrivilegesMapping = new ConcurrentHashMap<String, Set<AceBean>>();

@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 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);

Expand All @@ -65,7 +74,7 @@
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) {
Expand Down Expand Up @@ -107,26 +116,62 @@
+ msHumanReadable(stopWatch.getTime()));
}

private Set<String> filterReadOnlyPaths(Set<String> paths, InstallationLogger history, Session session) {

boolean isCompositeNodeStore = RuntimeHelper.isCompositeNodeStore(session);
if (isCompositeNodeStore) {
Set<String> pathsToKeep = new TreeSet<String>();
Set<String> readOnlyPaths = new TreeSet<String>();
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<String> filterForRelevantPaths(Set<String> paths, InstallationLogger history, Session session) {

Check warning on line 119 in accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/BaseAceBeanInstaller.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused method parameter "session".

See more on https://sonarcloud.io/project/issues?id=Netcentric_accesscontroltool&issues=AZzn9VF8AaNIPw8PLc98&open=AZzn9VF8AaNIPw8PLc98&pullRequest=869
if (serverType == ServerType.AEM_CLOUD_RUN) {
Set<String> 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) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ghenzler Should we also skip group installation during image build?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should be able to - but has to be done elsewhere and tested (this piece of code only looks at the paths)

Set<String> 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<String> {
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<String> removePathsWithPrefixes(Set<String> paths, String... prefixes) {
return removePathWithPredicate(paths, new PrefixesFilter(prefixes));
}

static Set<String> removePathsWithoutPrefixes(Set<String> paths, String... prefixes) {
return removePathWithPredicate(paths, Predicate.not(new PrefixesFilter(prefixes)));
}

static Set<String> removePathWithPredicate(Set<String> paths, Predicate<String> pathPredicate) {
Set<String> 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 */
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -25,13 +28,20 @@
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) {

Check warning on line 44 in accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/runtime/RuntimeHelper.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add the missing @Deprecated annotation.

See more on https://sonarcloud.io/project/issues?id=Netcentric_accesscontroltool&issues=AZzhf9GZCbU5pHOKTqn6&open=AZzhf9GZCbU5pHOKTqn6&pullRequest=869

Check warning on line 44 in accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/runtime/RuntimeHelper.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=Netcentric_accesscontroltool&issues=AZzhf9GZCbU5pHOKTqn5&open=AZzhf9GZCbU5pHOKTqn5&pullRequest=869

try {
String pathToCheck = "/apps";
Expand All @@ -52,7 +62,7 @@
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());
}
Expand All @@ -61,8 +71,13 @@
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

Check warning on line 79 in accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/runtime/RuntimeHelper.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add 'since' and/or 'forRemoval' arguments to the @Deprecated annotation.

See more on https://sonarcloud.io/project/issues?id=Netcentric_accesscontroltool&issues=AZzhf9GZCbU5pHOKTqn7&open=AZzhf9GZCbU5pHOKTqn7&pullRequest=869
public static boolean isCloudReadyInstance() {

Check warning on line 80 in accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/runtime/RuntimeHelper.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not forget to remove this deprecated code someday.

See more on https://sonarcloud.io/project/issues?id=Netcentric_accesscontroltool&issues=AZzhf9GZCbU5pHOKTqn8&open=AZzhf9GZCbU5pHOKTqn8&pullRequest=869

boolean isCloudReadyInstance = true;
Bundle[] bundles = FrameworkUtil.getBundle(RuntimeHelper.class).getBundleContext().getBundles();
for (Bundle bundle : bundles) {
Expand All @@ -74,4 +89,67 @@
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<String> 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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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")
Expand All @@ -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)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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 {
Expand Down
Loading