Skip to content

Commit

Permalink
Merge pull request #16846 from ekasumov/cors-filter-origins-regex
Browse files Browse the repository at this point in the history
Adding support for regular expressions in CORS origins configuration
  • Loading branch information
sberyozkin authored May 3, 2021
2 parents 15f8ebb + b787c9b commit 3d8c3d3
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 5 deletions.
6 changes: 3 additions & 3 deletions docs/src/main/asciidoc/http-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ following properties will be applied before passing the request on to its actual
[cols="<m,<m,<2",options="header"]
|===
|Property Name|Default|Description
|quarkus.http.cors.origins|*|The comma-separated list of origins allowed for CORS. The filter allows any origin if this is not set or set to '*'.
|quarkus.http.cors.origins|*|The comma-separated list of origins allowed for CORS. Values starting and ending with '/'' will be treated as regular expressions. The filter allows any origin if this is not set or set to '*'.
|quarkus.http.cors.methods|*|The comma-separated list of HTTP methods allowed for CORS. The filter allows any method if this is
not set or set to '*'.
|quarkus.http.cors.headers|*|The comma-separated list of HTTP headers allowed for CORS. The filter allows any header if this is
Expand All @@ -205,12 +205,12 @@ when the request’s credentials mode Request.credentials is “include”

include::duration-format-note.adoc[]

Here's what a full CORS filter configuration could look like:
Here's what a full CORS filter configuration could look like, including a regular expression defining an allowed origin:

[source, properties]
----
quarkus.http.cors=true
quarkus.http.cors.origins=http://foo.com,http://www.bar.io
quarkus.http.cors.origins=http://foo.com,http://www.bar.io,/https://([a-z0-9\\-_]+)\\.app\\.mydomain\\.com/
quarkus.http.cors.methods=GET,PUT,POST
quarkus.http.cors.headers=X-Custom
quarkus.http.cors.exposed-headers=Content-Disposition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ public class CORSFilter implements Handler<RoutingContext> {
// Must be static because the filter is created(deployed) at build time and runtime config is still not available
final CORSConfig corsConfig;

final List<Pattern> allowedOriginsRegex;

public CORSFilter(CORSConfig corsConfig) {
this.corsConfig = corsConfig;
this.allowedOriginsRegex = parseAllowedOriginsRegex(this.corsConfig.origins);
}

public static boolean isConfiguredWithWildcard(Optional<List<String>> optionalList) {
Expand All @@ -36,6 +39,48 @@ public static boolean isConfiguredWithWildcard(Optional<List<String>> optionalLi
return list.isEmpty() || (list.size() == 1 && "*".equals(list.get(0)));
}

/**
* Parse the provided allowed origins for any regexes
*
* @param allowedOrigins
* @return a list of compiled regular expressions. If none configured, and empty list is returned
*/
public static List<Pattern> parseAllowedOriginsRegex(Optional<List<String>> allowedOrigins) {
if (allowedOrigins == null || !allowedOrigins.isPresent()) {
return Collections.emptyList();
}

// extract configured origins and find any Regular Expressions
List<Pattern> allowOriginsRegex = new ArrayList<>();
for (String o : allowedOrigins.get()) {
if (o != null && o.startsWith("/") && o.endsWith("/")) {
allowOriginsRegex.add(Pattern.compile(o.substring(1, o.length() - 1)));
}
}

return allowOriginsRegex;
}

/**
* If any regular expression origins are configured, try to match on them.
* Regular expressions must begin and end with '/'
*
* @param allowedOrigins the configured regex origins.
* @param origin the specified origin
* @return true if any configured regular expressions match the specified origin, false otherwise
*/
public static boolean isOriginAllowedByRegex(List<Pattern> allowOriginsRegex, String origin) {
if (allowOriginsRegex == null) {
return false;
}
for (Pattern pattern : allowOriginsRegex) {
if (pattern.matcher(origin).matches()) {
return true;
}
}
return false;
}

private void processRequestedHeaders(HttpServerResponse response, String allowHeadersValue) {
if (isConfiguredWithWildcard(corsConfig.headers)) {
response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, allowHeadersValue);
Expand Down Expand Up @@ -103,7 +148,8 @@ public void handle(RoutingContext event) {
processRequestedHeaders(response, requestedHeaders);
}

boolean allowsOrigin = isConfiguredWithWildcard(corsConfig.origins) || corsConfig.origins.get().contains(origin);
boolean allowsOrigin = isConfiguredWithWildcard(corsConfig.origins) || corsConfig.origins.get().contains(origin)
|| isOriginAllowedByRegex(allowedOriginsRegex, origin);

if (allowsOrigin) {
response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
Expand Down Expand Up @@ -133,4 +179,4 @@ public void handle(RoutingContext event) {
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package io.quarkus.vertx.http.runtime.cors;

import static io.quarkus.vertx.http.runtime.cors.CORSFilter.isConfiguredWithWildcard;
import static io.quarkus.vertx.http.runtime.cors.CORSFilter.isOriginAllowedByRegex;
import static io.quarkus.vertx.http.runtime.cors.CORSFilter.parseAllowedOriginsRegex;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
Expand All @@ -22,4 +26,15 @@ public void isConfiguredWithWildcardTest() {
Assertions.assertFalse(isConfiguredWithWildcard(Optional.of(Collections.singletonList("http://localhost:8080/"))));
}

@Test
public void isOriginAllowedByRegexTest() {
Assertions.assertFalse(isOriginAllowedByRegex(Collections.emptyList(), "http://locahost:8080"));
Assertions.assertEquals(
parseAllowedOriginsRegex(Optional.of(Collections.singletonList("http://localhost:8080"))).size(),
0);
List<Pattern> regexList = parseAllowedOriginsRegex(
Optional.of(Collections.singletonList("/https://([a-z0-9\\-_]+)\\.app\\.mydomain\\.com/")));
Assertions.assertEquals(regexList.size(), 1);
Assertions.assertTrue(isOriginAllowedByRegex(regexList, "https://abc-123.app.mydomain.com"));
}
}

0 comments on commit 3d8c3d3

Please sign in to comment.