diff options
author | Valerij Fredriksen <valerijf@yahooinc.com> | 2023-06-06 15:52:41 +0200 |
---|---|---|
committer | Valerij Fredriksen <valerijf@yahooinc.com> | 2023-06-06 15:52:41 +0200 |
commit | a5c36c88fe03eb16908e7066df2be7fc08fef7ce (patch) | |
tree | e5732e52acca99978e1fd3bb0c3f803bfd8d8863 /jdisc-security-filters/src/main/java/com/yahoo/jdisc | |
parent | 212a1934ff38662183609827ac91a67a34179eb0 (diff) |
Allow subdomains in CORS filters
Diffstat (limited to 'jdisc-security-filters/src/main/java/com/yahoo/jdisc')
3 files changed, 45 insertions, 21 deletions
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cors/CorsLogic.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cors/CorsLogic.java index e261f420e1c..f24778d1241 100644 --- a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cors/CorsLogic.java +++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cors/CorsLogic.java @@ -2,15 +2,19 @@ package com.yahoo.jdisc.http.filter.security.cors; import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.regex.Pattern; /** * @author bjorncs */ class CorsLogic { - private CorsLogic() {} static final String CORS_PREFLIGHT_REQUEST_CACHE_TTL = Long.toString(Duration.ofDays(7).getSeconds()); @@ -25,23 +29,49 @@ class CorsLogic { "Vary", "*" ); - static Map<String, String> createCorsResponseHeaders(String requestOriginHeader, - Set<String> allowedOrigins) { + private final boolean allowAnyOrigin; + private final Set<String> allowedOrigins; + private final List<Pattern> allowedOriginPatterns; + private CorsLogic(boolean allowAnyOrigin, Set<String> allowedOrigins, List<Pattern> allowedOriginPatterns) { + this.allowAnyOrigin = allowAnyOrigin; + this.allowedOrigins = Set.copyOf(allowedOrigins); + this.allowedOriginPatterns = List.copyOf(allowedOriginPatterns); + } + + boolean originMatches(String origin) { + if (allowAnyOrigin) return true; + if (allowedOrigins.contains(origin)) return true; + return allowedOriginPatterns.stream().anyMatch(pattern -> pattern.matcher(origin).matches()); + } + + Map<String, String> createCorsResponseHeaders(String requestOriginHeader) { if (requestOriginHeader == null) return Map.of(); TreeMap<String, String> headers = new TreeMap<>(); - if (requestOriginMatchesAnyAllowed(requestOriginHeader, allowedOrigins)) + if (originMatches(requestOriginHeader)) headers.put(ALLOW_ORIGIN_HEADER, requestOriginHeader); headers.putAll(ACCESS_CONTROL_HEADERS); return headers; } - static Map<String, String> createCorsPreflightResponseHeaders(String requestOriginHeader, - Set<String> allowedOrigins) { - return createCorsResponseHeaders(requestOriginHeader, allowedOrigins); + Map<String, String> preflightResponseHeaders(String requestOriginHeader) { + return createCorsResponseHeaders(requestOriginHeader); } - private static boolean requestOriginMatchesAnyAllowed(String requestOrigin, Set<String> allowedUrls) { - return allowedUrls.stream().anyMatch(requestOrigin::equals) || allowedUrls.contains("*"); + static CorsLogic forAllowedOrigins(Collection<String> allowedOrigins) { + Set<String> allowedOriginsVerbatim = new HashSet<>(); + List<Pattern> allowedOriginPatterns = new ArrayList<>(); + for (String allowedOrigin : allowedOrigins) { + if (allowedOrigin.isBlank()) continue; + if (allowedOrigin.length() > 0) { + if ("*".equals(allowedOrigin)) + return new CorsLogic(true, Set.of(), List.of()); + else if (allowedOrigin.contains("*")) + allowedOriginPatterns.add(Pattern.compile(allowedOrigin.replace(".", "\\.").replace("*", ".*"))); + else + allowedOriginsVerbatim.add(allowedOrigin); + } + } + return new CorsLogic(false, allowedOriginsVerbatim, allowedOriginPatterns); } } diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cors/CorsPreflightRequestFilter.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cors/CorsPreflightRequestFilter.java index e2efd2d220c..935e738b5e3 100644 --- a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cors/CorsPreflightRequestFilter.java +++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cors/CorsPreflightRequestFilter.java @@ -10,8 +10,6 @@ import com.yahoo.jdisc.http.filter.DiscFilterRequest; import com.yahoo.jdisc.http.filter.SecurityRequestFilter; import com.yahoo.yolean.chain.Provides; -import java.util.Set; - import static com.yahoo.jdisc.http.HttpRequest.Method.OPTIONS; /** @@ -33,11 +31,11 @@ import static com.yahoo.jdisc.http.HttpRequest.Method.OPTIONS; */ @Provides("CorsPreflightRequestFilter") public class CorsPreflightRequestFilter implements SecurityRequestFilter { - private final Set<String> allowedUrls; + private final CorsLogic cors; @Inject public CorsPreflightRequestFilter(CorsFilterConfig config) { - this.allowedUrls = Set.copyOf(config.allowedUrls()); + this.cors = CorsLogic.forAllowedOrigins(config.allowedUrls()); } @Override @@ -46,8 +44,7 @@ public class CorsPreflightRequestFilter implements SecurityRequestFilter { return; HttpResponse response = HttpResponse.newInstance(Response.Status.OK); - String origin = discFilterRequest.getHeader("Origin"); - CorsLogic.createCorsPreflightResponseHeaders(origin, allowedUrls) + cors.preflightResponseHeaders(discFilterRequest.getHeader("Origin")) .forEach(response.headers()::put); ContentChannel cc = responseHandler.handleResponse(response); diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cors/CorsResponseFilter.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cors/CorsResponseFilter.java index f56965ea6a8..4b6c7211d11 100644 --- a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cors/CorsResponseFilter.java +++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cors/CorsResponseFilter.java @@ -8,9 +8,6 @@ import com.yahoo.jdisc.http.filter.RequestView; import com.yahoo.jdisc.http.filter.SecurityResponseFilter; import com.yahoo.yolean.chain.Provides; -import java.util.Set; - - /** * @author gv * @author Tony Vaagenes @@ -19,16 +16,16 @@ import java.util.Set; @Provides("CorsResponseFilter") public class CorsResponseFilter extends AbstractResource implements SecurityResponseFilter { - private final Set<String> allowedUrls; + private final CorsLogic cors; @Inject public CorsResponseFilter(CorsFilterConfig config) { - this.allowedUrls = Set.copyOf(config.allowedUrls()); + this.cors = CorsLogic.forAllowedOrigins(config.allowedUrls()); } @Override public void filter(DiscFilterResponse response, RequestView request) { - CorsLogic.createCorsResponseHeaders(request.getFirstHeader("Origin").orElse(null), allowedUrls) + cors.createCorsResponseHeaders(request.getFirstHeader("Origin").orElse(null)) .forEach(response::setHeader); } |