Skip to content

Commit

Permalink
#2980 Add possibility to evaluate redirect rule on request URI (#2981)
Browse files Browse the repository at this point in the history
* Add possibility to evaluate redirect rule on request URI instead of only resource path

Co-authored-by: Bart Thierens <[email protected]>
Co-authored-by: bart.thierens <[email protected]>
Co-authored-by: Yegor Kozlov <[email protected]>
  • Loading branch information
4 people authored Jan 19, 2023
1 parent 9fc3d6f commit 0de81d0
Show file tree
Hide file tree
Showing 16 changed files with 812 additions and 566 deletions.
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com)

### Changed

- #2982 - Add OSGi configuration option for CSV delimiters in reports
### Added
- #3016 - Added crawl delay
- #2980 - Redirect Manager: Allow evaluating of redirect rules based on request URI

### Added

- #2982 - Add OSGi configuration option for CSV delimiters in reports
- #3016 - Added crawl delay
- #3008 - Redirect Manager: Add "State" column
- #2977 - Redirect Manager: Add "Effective From" field

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,12 @@ public class RedirectFilter extends AnnotatedStandardMBean
@AttributeDefinition(name = "Preserve Query String", description = "Preserve query string in redirects", type = AttributeType.BOOLEAN)
boolean preserveQueryString() default true;

@AttributeDefinition(name = "Preserve Extension", description = "Whether to preserve extensions"
@AttributeDefinition(name = "Preserve Extension", description = "Whether to preserve extensions. "
+ "When this flag is checked (default), redirect filter will preserve the extension from the request, "
+ "e.g. append .html to the Location header. ", type = AttributeType.BOOLEAN)
boolean preserveExtension() default true;

@AttributeDefinition(name = "Evaluate Selectors", description = "Take into account selectors when evaluating redirects. "
+ "When this flag is unchecked (default), selectors are ignored and don't participate in rule matching", type = AttributeType.BOOLEAN)
@AttributeDefinition(name = "Evaluate Selectors", description = "(Deprecated) Use the Evaluate URI mode in redirect rule to capture selectors,", type = AttributeType.BOOLEAN)
boolean evaluateSelectors() default false;

@AttributeDefinition(name = "Additional Response Headers", description = "Optional response headers in the name:value format to apply on delivery,"
Expand Down Expand Up @@ -581,12 +580,12 @@ RedirectMatch match(SlingHttpServletRequest slingRequest) {
ValueMap properties = configResource.getValueMap();
String contextPrefix = properties.get(Redirects.CFG_PROP_CONTEXT_PREFIX, "");

RedirectMatch m = rules.match(resourcePath, contextPrefix);
RedirectMatch m = rules.match(resourcePath, contextPrefix, slingRequest);
if (m == null && mapUrls()) { // try mapped url
String mappedUrl= mapUrl(resourcePath, slingRequest); // https://www.mysite.com/en/page.html
if(!resourcePath.equals(mappedUrl)) { // don't bother if sling mappings are not defined for this path
String mappedPath = URI.create(mappedUrl).getPath(); // /en/page.html
m = rules.match(mappedPath);
m = rules.match(mappedPath, "", slingRequest);
}
}
return m;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package com.adobe.acs.commons.redirects.models;

import com.adobe.acs.commons.redirects.filter.RedirectFilter;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;

import java.util.Collection;
Expand All @@ -32,6 +33,8 @@
* A collection of redirect rules
*/
public class RedirectConfiguration {

private boolean nonRegexRequestURIRules = false;
/**
* path rules keyed by source, e.g. path1 -> path2.
* This makes lookup by path a O(1) operation
Expand Down Expand Up @@ -61,10 +64,15 @@ public RedirectConfiguration(Resource resource, String storageSuffix) {
if (rule.getRegex() != null) {
patternRules.put(rule.getRegex(), rule);
} else {
pathRules.put(normalizePath(rule.getSource()), rule);
// request URI rules are keyed without normalizing
if(rule.getEvaluateURI()){
nonRegexRequestURIRules = true;
pathRules.put(rule.getSource(), rule);
} else {
pathRules.put(normalizePath(rule.getSource()), rule);
}
}
}

}

/**
Expand All @@ -80,6 +88,10 @@ public static String normalizePath(String resourcePath) {
return resourcePath;
}

static String determinePathToEvaluate(String path, boolean evaluateURI, SlingHttpServletRequest request) {
return (evaluateURI && request != null) ? request.getRequestURI() : path;
}

public Map<String, RedirectRule> getPathRules() {
return pathRules;
}
Expand All @@ -106,10 +118,10 @@ public String getName() {
*
* @param requestPath the request to match
* @return match or null
* @see #match(String, String)
* @see #match(String, String, SlingHttpServletRequest)
*/
public RedirectMatch match(String requestPath) {
return match(requestPath, "");
return match(requestPath, "", null);
}

/**
Expand All @@ -120,19 +132,27 @@ public RedirectMatch match(String requestPath) {
* <li>Match by a regular expression. This is O(N) linear lookup in a list of rules keyed by their regex patterns</li>
* </ol>
*
* @param requestPath the request to match
* @param resourcePath the request to match
* @param contextPrefix the optional context prefix to take into account
* @param request the current sling request
* @return match or null
*/
public RedirectMatch match(String requestPath, String contextPrefix) {
String normalizedPath = normalizePath(requestPath);
public RedirectMatch match(String resourcePath, String contextPrefix, SlingHttpServletRequest request) {
String normalizedPath = normalizePath(resourcePath);
RedirectMatch match = null;
RedirectRule rule = getPathRule(normalizedPath, contextPrefix);
if(rule == null && hasNonRegexRequestURIRules()){
// there are request URI rules. Check is any mathes
String pathToEvaluate = determinePathToEvaluate(normalizedPath, true, request);
rule = getPathRule(pathToEvaluate, contextPrefix);
}
if (rule != null) {
match = new RedirectMatch(rule, null);
} else {
for (Map.Entry<Pattern, RedirectRule> entry : getPatternRules().entrySet()) {
Matcher m = getRuleMatch(entry.getKey(), normalizedPath, contextPrefix);
boolean evaluateURI = entry.getValue().getEvaluateURI();
String pathToEvaluate = determinePathToEvaluate(normalizedPath, evaluateURI, request);
Matcher m = getRuleMatch(entry.getKey(), pathToEvaluate, contextPrefix);
if (m.matches()) {
match = new RedirectMatch(entry.getValue(), m);
break;
Expand All @@ -145,25 +165,25 @@ public RedirectMatch match(String requestPath, String contextPrefix) {
/**
* Utility method that gets the pattern rule taking an optional context prefix into account
* @param rulePattern the regex pattern to match the path
* @param normalizedPath the normalized path
* @param pathToEvaluate the path to evaluate for redirects
* @param contextPrefix the optional context prefix
* @return the matcher associated with the rule
*/
private Matcher getRuleMatch(Pattern rulePattern, String normalizedPath, String contextPrefix) {
private Matcher getRuleMatch(Pattern rulePattern, String pathToEvaluate, String contextPrefix) {
if("".equals(contextPrefix)) {
return rulePattern.matcher(normalizedPath);
return rulePattern.matcher(pathToEvaluate);
} else {
//we add the context prefix to the pattern since a pattern might be too broad otherwise,
//i.e. "/(.*)" will match anything
if(!rulePattern.toString().startsWith(contextPrefix)) {
rulePattern = RedirectRule.toRegex(contextPrefix + rulePattern.toString());
}
Matcher matcher = rulePattern.matcher(normalizedPath);
Matcher matcher = rulePattern.matcher(pathToEvaluate);
if(!matcher.matches()) {
if (normalizedPath.startsWith(contextPrefix)) {
matcher = rulePattern.matcher(normalizedPath.replace(contextPrefix, ""));
if (pathToEvaluate.startsWith(contextPrefix)) {
matcher = rulePattern.matcher(pathToEvaluate.replace(contextPrefix, ""));
} else {
matcher = rulePattern.matcher(contextPrefix + normalizedPath);
matcher = rulePattern.matcher(contextPrefix + pathToEvaluate);
}
}
return matcher;
Expand Down Expand Up @@ -192,4 +212,8 @@ private RedirectRule getPathRule(String normalizedPath, String contextPrefix) {
}
}

private boolean hasNonRegexRequestURIRules() {
return this.nonRegexRequestURIRules;
}

}
13 changes: 11 additions & 2 deletions bundle/src/main/java/com/adobe/acs/commons/redirects/models/RedirectRule.java
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public class RedirectRule {
public static final String EFFECTIVE_FROM_PROPERTY_NAME = "effectiveFrom";
public static final String NOTE_PROPERTY_NAME = "note";
public static final String CONTEXT_PREFIX_IGNORED_PROPERTY_NAME = "contextPrefixIgnored";
public static final String EVALUATE_URI_PROPERTY_NAME = "evaluateURI";
public static final String CREATED_PROPERTY_NAME = "jcr:created";
public static final String CREATED_BY_PROPERTY_NAME = "jcr:createdBy";
public static final String MODIFIED_PROPERTY_NAME = "jcr:lastModified";
Expand All @@ -69,6 +70,9 @@ public class RedirectRule {
@ValueMapValue(injectionStrategy = InjectionStrategy.REQUIRED)
private int statusCode;

@ValueMapValue
private boolean evaluateURI;

@ValueMapValue
private Calendar untilDate;

Expand Down Expand Up @@ -134,6 +138,10 @@ public int getStatusCode() {
return statusCode;
}

public boolean getEvaluateURI() {
return evaluateURI;
}

public String getCreatedBy() {
return createdBy;
}
Expand Down Expand Up @@ -190,9 +198,9 @@ public List<Tag> getTags() {

@Override
public String toString() {
return String.format("RedirectRule{source='%s', target='%s', statusCode=%s, untilDate=%s, effectiveFrom=%s, note=%s, "
return String.format("RedirectRule{source='%s', target='%s', statusCode=%s, untilDate=%s, effectiveFrom=%s, note=%s, evaluateURI=%s,"
+ "contextPrefixIgnored=%s, tags=%s, created=%s, createdBy=%s, modified=%s, modifiedBy=%s}",
source, target, statusCode, untilDate, effectiveFrom, note, contextPrefixIgnored,
source, target, statusCode, untilDate, effectiveFrom, note, evaluateURI, contextPrefixIgnored,
Arrays.toString(tagIds), created, createdBy, modified, modifiedBy);
}

Expand Down Expand Up @@ -238,6 +246,7 @@ static Pattern toRegex(String src) {
}

/**
* @return whether the rule has expired, i.e. the 'untilDate' property is before the current time
* ----[effectiveFrom]---[now]---[untilDate]--->
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,14 @@ static XSSFWorkbook export(Collection<RedirectRule> rules) {
headerRow.createCell(2).setCellValue("Status Code");
headerRow.createCell(3).setCellValue("Off Time");
headerRow.createCell(4).setCellValue("Notes");
headerRow.createCell(5).setCellValue("Ignore Context Prefix");
headerRow.createCell(6).setCellValue("Tags");
headerRow.createCell(7).setCellValue("Created");
headerRow.createCell(8).setCellValue("Created By");
headerRow.createCell(9).setCellValue("Modified");
headerRow.createCell(10).setCellValue("Modified By");
headerRow.createCell(11).setCellValue("On Time");
headerRow.createCell(5).setCellValue("Evaluate URI");
headerRow.createCell(6).setCellValue("Ignore Context Prefix");
headerRow.createCell(7).setCellValue("Tags");
headerRow.createCell(8).setCellValue("Created");
headerRow.createCell(9).setCellValue("Created By");
headerRow.createCell(10).setCellValue("Modified");
headerRow.createCell(11).setCellValue("Modified By");
headerRow.createCell(12).setCellValue("On Time");
for (Cell cell : headerRow) {
cell.setCellStyle(headerStyle);
}
Expand All @@ -130,51 +131,53 @@ static XSSFWorkbook export(Collection<RedirectRule> rules) {
cell.setCellStyle(dateStyle);
}
row.createCell(4).setCellValue(rule.getNote());
row.createCell(5).setCellValue(rule.getContextPrefixIgnored());
row.createCell(5).setCellValue(rule.getEvaluateURI());
row.createCell(6).setCellValue(rule.getContextPrefixIgnored());

Cell cell6 = row.createCell(6);
Cell cell6 = row.createCell(7);
String[] tagIds = rule.getTagIds();
if(tagIds != null) {
cell6.setCellValue(String.join("\n", tagIds));
}
cell6.setCellStyle(cellWrapStyle);

Cell cell7 = row.createCell(7);
Cell cell7 = row.createCell(8);
cell7.setCellValue(rule.getCreated());
cell7.setCellStyle(dateStyle);

Cell cell8 = row.createCell(8);
Cell cell8 = row.createCell(9);
cell8.setCellValue(rule.getCreatedBy());
cell8.setCellStyle(lockedCellStyle);

Cell cell9 = row.createCell(9);
Cell cell9 = row.createCell(10);
cell9.setCellValue(rule.getModified());
cell9.setCellStyle(dateStyle);

Cell cell10 = row.createCell(10);
Cell cell10 = row.createCell(11);
cell10.setCellValue(rule.getModifiedBy());
cell10.setCellStyle(lockedCellStyle);

Calendar effectiveFrom = rule.getEffectiveFrom();
if (effectiveFrom != null) {
Cell cell = row.createCell(11);
Cell cell = row.createCell(12);
cell.setCellValue(effectiveFrom);
cell.setCellStyle(dateStyle);
}
}
}
sheet.setAutoFilter(new CellRangeAddress(0, rownum - 1, 0, 10));
sheet.setColumnWidth(0, 256 * 50);
sheet.setColumnWidth(1, 256 * 50);
sheet.setColumnWidth(2, 256 * 15);
sheet.setColumnWidth(3, 256 * 12);
sheet.setColumnWidth(4, 256 * 100);
sheet.setColumnWidth(5, 256 * 20);
sheet.setColumnWidth(6, 256 * 25);
sheet.setColumnWidth(7, 256 * 12);
sheet.setColumnWidth(8, 256 * 30);
sheet.setColumnWidth(9, 256 * 12);
sheet.setColumnWidth(10, 256 * 30);
sheet.setColumnWidth(11, 256 * 12);
sheet.setColumnWidth(6, 256 * 20);
sheet.setColumnWidth(7, 256 * 25);
sheet.setColumnWidth(8, 256 * 12);
sheet.setColumnWidth(9, 256 * 30);
sheet.setColumnWidth(10, 256 * 12);
sheet.setColumnWidth(11, 256 * 30);
sheet.setColumnWidth(12, 256 * 12);

return wb;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,21 +190,24 @@ private Map<String, Object> readRedirect(Row row){
untilDate.setTime(c3.getDateCellValue());
props.put(RedirectRule.UNTIL_DATE_PROPERTY_NAME, untilDate);
}
Cell c11 = row.getCell(11);
if (DateUtil.isCellDateFormatted(c11)) {
Cell c12 = row.getCell(12);
if (DateUtil.isCellDateFormatted(c12)) {
Calendar effectiveFrom = Calendar.getInstance();
effectiveFrom.setTime(c11.getDateCellValue());
effectiveFrom.setTime(c12.getDateCellValue());
props.put(RedirectRule.EFFECTIVE_FROM_PROPERTY_NAME, effectiveFrom);
}
Cell c4 = row.getCell(4);
if(c4 != null) {
props.put(RedirectRule.NOTE_PROPERTY_NAME, c4.getStringCellValue());
}
Cell c5 = row.getCell(5);
boolean ignoreContextPrefix = (c5 != null && c5.getBooleanCellValue());
props.put(RedirectRule.CONTEXT_PREFIX_IGNORED_PROPERTY_NAME, ignoreContextPrefix);
boolean evaluateURI = (c5 != null && c5.getBooleanCellValue());
props.put(RedirectRule.EVALUATE_URI_PROPERTY_NAME, evaluateURI);
Cell c6 = row.getCell(6);
String[] tagIds = c6 == null ? null : c6.getStringCellValue().split("\n");
boolean ignoreContextPrefix = (c6 != null && c6.getBooleanCellValue());
props.put(RedirectRule.CONTEXT_PREFIX_IGNORED_PROPERTY_NAME, ignoreContextPrefix);
Cell c7 = row.getCell(7);
String[] tagIds = c7 == null ? null : c7.getStringCellValue().split("\n");
props.put(RedirectRule.TAGS_PROPERTY_NAME, tagIds);
return props;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ public RedirectResourceBuilder setStatusCode(int statusCode) {
return this;
}

public RedirectResourceBuilder setEvaluateURI(boolean evaluateURI) {
props.put(EVALUATE_URI_PROPERTY_NAME, evaluateURI);
return this;
}

public RedirectResourceBuilder setNotes(String note) {
props.put(NOTE_PROPERTY_NAME, note);
return this;
Expand Down
Loading

0 comments on commit 0de81d0

Please sign in to comment.