Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SLING-10447 Improve the querys that are used to load vanity paths, by specifying path restrictions #46

Expand Up @@ -18,19 +18,10 @@
*/
package org.apache.sling.resourceresolver.impl;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

import org.jetbrains.annotations.NotNull;

import org.apache.commons.collections4.BidiMap;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.path.Path;
import org.apache.sling.resourceresolver.impl.console.ResourceResolverWebConsolePlugin;
import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
import org.apache.sling.resourceresolver.impl.helper.ResourceResolverControl;
Expand All @@ -41,11 +32,25 @@
import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker;
import org.apache.sling.serviceusermapping.ServiceUserMapper;
import org.apache.sling.spi.resource.provider.ResourceProvider;
import org.jetbrains.annotations.NotNull;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* The <code>CommonResourceResolverFactoryImpl</code> is a singleton
* implementing the shared/common functionality of all resource
Expand Down Expand Up @@ -438,33 +443,6 @@ public boolean hasVanityPathPrecedence() {
return this.activator.hasVanityPathPrecedence();
}

@Override
public Path[] getObservationPaths() {
return this.activator.getObservationPaths();
}

@Override
public List<VanityPathConfig> getVanityPathConfig() {
final String[] includes = this.activator.getVanityPathWhiteList();
final String[] excludes = this.activator.getVanityPathBlackList();
if ( includes == null && excludes == null ) {
return null;
}
final List<VanityPathConfig> configs = new ArrayList<>();
if ( includes != null ) {
for(final String val : includes) {
configs.add(new VanityPathConfig(val, false));
}
}
if ( excludes != null ) {
for(final String val : excludes) {
configs.add(new VanityPathConfig(val, true));
}
}
Collections.sort(configs);
return configs;
}

@Override
public Set<String> getAllowedAliasLocations() {
return this.activator.getAllowedAliasLocations();
Expand Down Expand Up @@ -516,6 +494,16 @@ public Map<String, Object> getServiceUserAuthenticationInfo(final String subServ
return authenticationInfo;
}

@Override
henrykuijpers marked this conversation as resolved.
Show resolved Hide resolved
public @NotNull Set<String> getAllowedVanityPathLocations() {
return activator.getAllowedVanityPathLocations();
}

@Override
public @NotNull Set<String> getExcludedVanityPathLocations() {
return activator.getExcludedVanityPathLocations();
}

/**
* Extension of a weak reference to be able to get the control object
* that is used for cleaning up.
Expand Down
Expand Up @@ -18,27 +18,23 @@
*/
package org.apache.sling.resourceresolver.impl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;


import org.apache.commons.collections4.BidiMap;
import org.apache.commons.collections4.bidimap.TreeBidiMap;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.ResourceDecorator;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.path.Path;
import org.apache.sling.api.resource.runtime.RuntimeService;
import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
import org.apache.sling.resourceresolver.impl.mapping.Mapping;
import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider;
import org.apache.sling.resourceresolver.impl.observation.ResourceChangeListenerWhiteboard;
import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker;
import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker.ChangeListener;
import org.apache.sling.resourceresolver.impl.providers.RuntimeServiceImpl;
import org.apache.sling.serviceusermapping.ServiceUserMapper;
import org.jetbrains.annotations.NotNull;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
Expand All @@ -56,6 +52,22 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* The <code>ResourceResolverFactoryActivator/code> keeps track of required services for the
* resource resolver factory.
Expand Down Expand Up @@ -126,14 +138,13 @@ private static final class FactoryRegistration {
@SuppressWarnings("java:S3077")
private volatile Set<String> allowedAliasLocations = Collections.emptySet();

/** Vanity path whitelist */
private volatile String[] vanityPathWhiteList;

/** Vanity path blacklist */
private volatile String[] vanityPathBlackList;
/** Allowed vanity path locations */
@SuppressWarnings("java:S3077")
private volatile Set<String> allowedVanityPathLocations = Collections.emptySet();

/** Observation paths */
private volatile Path[] observationPaths;
/** Excluded vanity path locations */
@SuppressWarnings("java:S3077")
private volatile Set<String> excludedVanityPathLocations = Collections.emptySet();

private final FactoryPreconditions preconds = new FactoryPreconditions();

Expand Down Expand Up @@ -211,12 +222,14 @@ public boolean isLogUnclosedResourceResolvers() {
return this.config.resource_resolver_log_unclosed();
}

public String[] getVanityPathWhiteList() {
return this.vanityPathWhiteList;
@NotNull
public Set<String> getAllowedVanityPathLocations() {
return this.allowedVanityPathLocations;
}

public String[] getVanityPathBlackList() {
return this.vanityPathBlackList;
@NotNull
public Set<String> getExcludedVanityPathLocations() {
return this.excludedVanityPathLocations;
}

public boolean hasVanityPathPrecedence() {
Expand All @@ -239,10 +252,6 @@ public boolean shouldLogResourceResolverClosing() {
return this.config.resource_resolver_log_closing();
}

public Path[] getObservationPaths() {
return this.observationPaths;
}

// ---------- SCR Integration ---------------------------------------------

/**
Expand Down Expand Up @@ -300,12 +309,6 @@ protected void activate(final BundleContext bundleContext, final ResourceResolve
mapRoot = config.resource_resolver_map_location();
mapRootPrefix = mapRoot + '/';

final String[] paths = config.resource_resolver_map_observation();
this.observationPaths = new Path[paths.length];
for(int i=0;i<paths.length;i++) {
this.observationPaths[i] = new Path(paths[i]);
}

// optimize alias path allow list
String[] aliasLocationsPrefix = config.resource_resolver_allowed_alias_locations();
if ( aliasLocationsPrefix != null ) {
Expand All @@ -326,42 +329,16 @@ protected void activate(final BundleContext bundleContext, final ResourceResolve
}
}

// vanity path white list
this.vanityPathWhiteList = null;
String[] vanityPathPrefixes = config.resource_resolver_vanitypath_whitelist();
if ( vanityPathPrefixes != null ) {
final List<String> prefixList = new ArrayList<>();
for(final String value : vanityPathPrefixes) {
if ( value.trim().length() > 0 ) {
if ( value.trim().endsWith("/") ) {
prefixList.add(value.trim());
} else {
prefixList.add(value.trim() + "/");
}
}
}
if ( prefixList.size() > 0 ) {
this.vanityPathWhiteList = prefixList.toArray(new String[prefixList.size()]);
}
}
// vanity path black list
this.vanityPathBlackList = null;
vanityPathPrefixes = config.resource_resolver_vanitypath_blacklist();
if ( vanityPathPrefixes != null ) {
final List<String> prefixList = new ArrayList<>();
for(final String value : vanityPathPrefixes) {
if ( value.trim().length() > 0 ) {
if ( value.trim().endsWith("/") ) {
prefixList.add(value.trim());
} else {
prefixList.add(value.trim() + "/");
}
}
}
if ( prefixList.size() > 0 ) {
this.vanityPathBlackList = prefixList.toArray(new String[prefixList.size()]);
}
}
this.allowedVanityPathLocations = Collections.unmodifiableSet(Arrays
.stream(ArrayUtils.nullToEmpty(config.resource_resolver_vanitypath_whitelist()))
.map(String::trim)
.map(value -> StringUtils.appendIfMissing(value, String.valueOf('/')))
.collect(Collectors.toSet()));
this.excludedVanityPathLocations = Collections.unmodifiableSet(Stream.concat(Arrays
.stream(ArrayUtils.nullToEmpty(config.resource_resolver_vanitypath_blacklist())), Stream.of("/jcr:system"))
.map(String::trim)
.map(value -> StringUtils.appendIfMissing(value, String.valueOf('/')))
.collect(Collectors.toSet()));

// check for required property
Set<String> requiredResourceProvidersLegacy = getStringSet(config.resource_resolver_required_providers());
Expand Down
Expand Up @@ -112,11 +112,6 @@
"the ResourceResolver mapping. The default value is /etc/map.")
String resource_resolver_map_location() default MapEntries.DEFAULT_MAP_ROOT;

@AttributeDefinition(name = "Mapping Observation",
henrykuijpers marked this conversation as resolved.
Show resolved Hide resolved
description = "The paths where vanity paths or aliases can be found. These paths are used to " +
"listen for resource events.")
String[] resource_resolver_map_observation() default "/";

@AttributeDefinition(name = "Default Vanity Path Redirect Status",
description = "The default status code used when a sling:vanityPath is configured to redirect " +
"and does not have a specific status code associated with it " +
Expand Down
Expand Up @@ -16,13 +16,12 @@
*/
package org.apache.sling.resourceresolver.impl.mapping;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.path.Path;
import org.jetbrains.annotations.NotNull;

import java.util.Map;
import java.util.Set;

/**
* Internal interface representing the additional methods
Expand All @@ -37,8 +36,6 @@ public interface MapConfigurationProvider extends ResourceResolverFactory {

boolean isMapConfiguration(String path);

Path[] getObservationPaths();

Map<?, ?> getVirtualURLMap();

Mapping[] getMappings();
Expand All @@ -58,32 +55,24 @@ public interface MapConfigurationProvider extends ResourceResolverFactory {
boolean hasVanityPathPrecedence();

Map<String, Object> getServiceUserAuthenticationInfo(final String subServiceName) throws LoginException;

public class VanityPathConfig implements Comparable<VanityPathConfig> {
public final boolean isExclude;
public final String prefix;

public VanityPathConfig(final String prefix, final boolean isExclude) {
this.prefix = prefix;
this.isExclude = isExclude;
}

@Override
public int compareTo(VanityPathConfig o2) {
return new Integer(o2.prefix.length()).compareTo(this.prefix.length());
}
}

/**
* A list of white and black list prefixes all ending with a slash.
* If <code>null</code> is returned, all paths are allowed.
* A set of allowed prefixes all ending with a slash.
* If empty set is returned, all paths are allowed.
*/
@NotNull
Set<String> getAllowedVanityPathLocations();

/**
* A set of excluded prefixes all ending with a slash.
* If empty set is returned, all paths are allowed.
*/
List<VanityPathConfig> getVanityPathConfig();
@NotNull
Set<String> getExcludedVanityPathLocations();

/**
* A set of allow prefixes all ending with a slash.
* If empty set is returned, all paths are allowed.
* @return
*/
Set<String> getAllowedAliasLocations();
}