Skip to content

Commit

Permalink
Support coma-delimited format for origin values
Browse files Browse the repository at this point in the history
Closes gh-27606
  • Loading branch information
rstoyanchev committed Sep 27, 2022
1 parent d516667 commit badba7c
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
Expand Down Expand Up @@ -120,9 +119,16 @@ public CorsConfiguration(CorsConfiguration other) {


/**
* A list of origins for which cross-origin requests are allowed. Values may
* be a specific domain, e.g. {@code "https://domain1.com"}, or the CORS
* defined special value {@code "*"} for all origins.
* A list of origins for which cross-origin requests are allowed where each
* value may be one of the following:
* <ul>
* <li>a specific domain, e.g. {@code "https://domain1.com"}
* <li>coma-delimited list of specific domains, e.g.
* {@code "https://a1.com,https://a2.com"}; this is convenient when a value
* is resolved through a property placeholder, e.g. {@code "${origin}"};
* note that such placeholders must be resolved externally.
* <li>the CORS defined special value {@code "*"} for all origins
* </ul>
* <p>For matched pre-flight and actual requests the
* {@code Access-Control-Allow-Origin} response header is set either to the
* matched domain value or to {@code "*"}. Keep in mind however that the
Expand All @@ -135,8 +141,15 @@ public CorsConfiguration(CorsConfiguration other) {
* {@code @CrossOrigin}, via {@link #applyPermitDefaultValues()}.
*/
public void setAllowedOrigins(@Nullable List<String> origins) {
this.allowedOrigins = (origins == null ? null :
origins.stream().filter(Objects::nonNull).map(this::trimTrailingSlash).collect(Collectors.toList()));
if (origins == null) {
this.allowedOrigins = null;
}
else {
this.allowedOrigins = new ArrayList<>(origins.size());
for (String origin : origins) {
addAllowedOrigin(origin);
}
}
}

private String trimTrailingSlash(String origin) {
Expand Down Expand Up @@ -164,8 +177,10 @@ public void addAllowedOrigin(@Nullable String origin) {
else if (this.allowedOrigins == DEFAULT_PERMIT_ALL && CollectionUtils.isEmpty(this.allowedOriginPatterns)) {
setAllowedOrigins(DEFAULT_PERMIT_ALL);
}
origin = trimTrailingSlash(origin);
this.allowedOrigins.add(origin);
parseComaDelimitedOrigin(origin, value -> {
value = trimTrailingSlash(value);
this.allowedOrigins.add(value);
});
}

/**
Expand All @@ -178,12 +193,16 @@ else if (this.allowedOrigins == DEFAULT_PERMIT_ALL && CollectionUtils.isEmpty(th
* domain1.com on port 8080 or port 8081
* <li>{@literal https://*.domain1.com:[*]} -- domains ending with
* domain1.com on any port, including the default port
* <li>coma-delimited list of patters, e.g.
* {@code "https://*.a1.com,https://*.a2.com"}; this is convenient when a
* value is resolved through a property placeholder, e.g. {@code "${origin}"};
* note that such placeholders must be resolved externally.
* </ul>
* <p>In contrast to {@link #setAllowedOrigins(List) allowedOrigins} which
* only supports "*" and cannot be used with {@code allowCredentials}, when
* an allowedOriginPattern is matched, the {@code Access-Control-Allow-Origin}
* response header is set to the matched origin and not to {@code "*"} nor
* to the pattern. Therefore allowedOriginPatterns can be used in combination
* to the pattern. Therefore, allowedOriginPatterns can be used in combination
* with {@link #setAllowCredentials} set to {@code true}.
* <p>By default this is not set.
* @since 5.3
Expand Down Expand Up @@ -226,10 +245,40 @@ public void addAllowedOriginPattern(@Nullable String originPattern) {
if (this.allowedOriginPatterns == null) {
this.allowedOriginPatterns = new ArrayList<>(4);
}
originPattern = trimTrailingSlash(originPattern);
this.allowedOriginPatterns.add(new OriginPattern(originPattern));
if (this.allowedOrigins == DEFAULT_PERMIT_ALL) {
this.allowedOrigins = null;
parseComaDelimitedOrigin(originPattern, value -> {
value = trimTrailingSlash(value);
this.allowedOriginPatterns.add(new OriginPattern(value));
if (this.allowedOrigins == DEFAULT_PERMIT_ALL) {
this.allowedOrigins = null;
}
});
}

private static void parseComaDelimitedOrigin(String rawValue, Consumer<String> valueConsumer) {
if (rawValue.indexOf(',') == -1) {
valueConsumer.accept(rawValue);
return;
}
int start = 0;
boolean withinPortRange = false;
for (int current = 0; current < rawValue.length(); current++) {
switch (rawValue.charAt(current)) {
case '[':
withinPortRange = true;
break;
case ']':
withinPortRange = false;
break;
case ',':
if (!withinPortRange) {
valueConsumer.accept(rawValue.substring(start, current).trim());
start = current + 1;
}
break;
}
}
if (start < rawValue.length()) {
valueConsumer.accept(rawValue.substring(start));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -291,6 +291,11 @@ void checkOriginAllowed() {
config.setAllowCredentials(true);
assertThatIllegalArgumentException().isThrownBy(() -> config.checkOrigin("https://domain.com"));

// coma-delimited origins list
config.setAllowedOrigins(Collections.singletonList("https://a1.com,https://a2.com"));
assertThat(config.checkOrigin("https://a1.com")).isEqualTo("https://a1.com");
assertThat(config.checkOrigin("https://a2.com/")).isEqualTo("https://a2.com/");

// specific origin matches Origin header with or without trailing "/"
config.setAllowedOrigins(Collections.singletonList("https://domain.com"));
assertThat(config.checkOrigin("https://domain.com")).isEqualTo("https://domain.com");
Expand Down Expand Up @@ -344,6 +349,12 @@ void checkOriginPatternAllowed() {
assertThat(config.checkOrigin("https://example.specific.port.com:8081")).isEqualTo("https://example.specific.port.com:8081");
assertThat(config.checkOrigin("https://example.specific.port.com:1234")).isNull();

config.addAllowedOriginPattern("https://*.a1.com:[8080,8081],https://*.a2.com");
assertThat(config.checkOrigin("https://example.a1.com:8080")).isEqualTo("https://example.a1.com:8080");
assertThat(config.checkOrigin("https://example.a1.com:8081")).isEqualTo("https://example.a1.com:8081");
assertThat(config.checkOrigin("https://example.a1.com:8082")).isNull();
assertThat(config.checkOrigin("https://example.a2.com")).isEqualTo("https://example.a2.com");

config.setAllowCredentials(false);
assertThat(config.checkOrigin("https://example.domain.com")).isEqualTo("https://example.domain.com");
}
Expand Down

0 comments on commit badba7c

Please sign in to comment.