Skip to content

Commit

Permalink
4: Adds context path whitelist configuration to ToolsConfigPagePersis…
Browse files Browse the repository at this point in the history
…tenceStrategy (#5)
  • Loading branch information
cnagel authored Jul 1, 2024
1 parent 7516f6e commit 1ce2929
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 1 deletion.
4 changes: 4 additions & 0 deletions changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
<action type="update" dev="sseifert">
Switch to AEM 6.5.17 as minimum version.
</action>
<action type="update" dev="cnagel" issue="4">
Adds context path allow list to ToolsConfigPagePersistenceStrategy configuration.
This is a (minor) breaking change - by default only configurations stored in /content will work with this strategy.
</action>
</release>

<release version="1.9.4" date="2023-10-17">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ public class ToolsConfigPagePersistenceStrategy implements ConfigurationPersiste
description = "Relative path to the configuration page content.")
String relativeConfigPath() default "/tools/config/jcr:content";

@AttributeDefinition(name = "Context path allow list",
description = "Expression to match context paths. Context paths matching this expression are allowed.")
String contextPathRegex() default "^/content(/.+)$";

}

private static final String DEFAULT_CONFIG_NODE_TYPE = NT_UNSTRUCTURED;
Expand All @@ -111,6 +115,7 @@ public class ToolsConfigPagePersistenceStrategy implements ConfigurationPersiste

private boolean enabled;
private Pattern configPathPattern;
private Pattern contextPathPattern;
private Config config;

@Reference
Expand All @@ -126,6 +131,7 @@ public class ToolsConfigPagePersistenceStrategy implements ConfigurationPersiste
void activate(Config value) {
this.enabled = value.enabled();
this.configPathPattern = loadConfigPathPattern(value);
this.contextPathPattern = loadContextPathPattern(value);
this.config = value;
}

Expand All @@ -136,6 +142,13 @@ void activate(Config value) {
: null;
}

private @Nullable Pattern loadContextPathPattern(Config value) {
String contextPathRegex = value.contextPathRegex();
return enabled && StringUtils.isNotBlank(contextPathRegex)
? Pattern.compile(contextPathRegex)
: null;
}

@Override
public Resource getResource(@NotNull Resource resource) {
if (!enabled || !isConfigPagePath(resource.getPath())) {
Expand Down Expand Up @@ -298,7 +311,11 @@ private String checkPath(final ContextResource contextResource, final String che

@SuppressWarnings("unused")
private boolean isEnabledAndParamsValid(final Resource contentResource, final Collection<String> bucketNames, final String configName) {
return enabled && contentResource != null;
return enabled && contentResource != null && isContextPathAllowed(contentResource.getPath());
}

private boolean isContextPathAllowed(String contextPath) {
return contextPathPattern == null || contextPathPattern.matcher(contextPath).matches();
}

private String buildResourcePath(String path, String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,21 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.Calendar;
import java.util.List;
import java.util.Objects;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.caconfig.ConfigurationBuilder;
import org.apache.sling.caconfig.management.ConfigurationManager;
import org.apache.sling.caconfig.resource.spi.ConfigurationResourceResolvingStrategy;
import org.apache.sling.caconfig.resource.spi.ContextPathStrategy;
import org.apache.sling.caconfig.resource.spi.ContextResource;
import org.apache.sling.hamcrest.ResourceMatchers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -327,4 +335,20 @@ void testSimpleConfigWithCQLastModified() throws Exception {
assertEquals(5, config.intParam());
}

@Test
void shouldNotAllowContextPathOutsideOfConfiguredPattern() {
// create context root resource not in /conf
Resource contextRootResource = context.create().resource("/conf/global");
// allow context root resource in context path strategy
ContextPathStrategy contextPathStrategy = mock(ContextPathStrategy.class);
context.registerService(ContextPathStrategy.class, contextPathStrategy);
ContextResource contextResource = new ContextResource(contextRootResource, "/conf/global", 0);
when(contextPathStrategy.findContextResources(any())).then((inv) -> List.of(contextResource).iterator());
// load tools config page persistence strategy
ConfigurationResourceResolvingStrategy strategy = Objects.requireNonNull(context.getService(ConfigurationResourceResolvingStrategy.class));
String configPath = strategy.getResourcePath(contextRootResource, "sling:configs", "cloudconfigs/translation");
// should not return a config in /conf
assertNull(configPath);
}

}

0 comments on commit 1ce2929

Please sign in to comment.