mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-03-06 21:39:25 +00:00
Leave Issuer As String
Since StringOrURI is a valid issuer, MappedJwtClaimSetConverter and JwtIssuerValidator no longer assume it. Issue: gh-6073
This commit is contained in:
parent
c70b65c5df
commit
19649db9ce
@ -15,9 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.security.oauth2.jwt;
|
package org.springframework.security.oauth2.jwt;
|
||||||
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
||||||
@ -46,14 +43,7 @@ public final class JwtIssuerValidator implements OAuth2TokenValidator<Jwt> {
|
|||||||
*/
|
*/
|
||||||
public JwtIssuerValidator(String issuer) {
|
public JwtIssuerValidator(String issuer) {
|
||||||
Assert.notNull(issuer, "issuer cannot be null");
|
Assert.notNull(issuer, "issuer cannot be null");
|
||||||
|
this.issuer = issuer;
|
||||||
try {
|
|
||||||
this.issuer = new URL(issuer).toString();
|
|
||||||
} catch (MalformedURLException ex) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Invalid Issuer URL " + issuer + " : " + ex.getMessage(),
|
|
||||||
ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.security.oauth2.jwt;
|
package org.springframework.security.oauth2.jwt;
|
||||||
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@ -42,7 +41,7 @@ public final class MappedJwtClaimSetConverter
|
|||||||
implements Converter<Map<String, Object>, Map<String, Object>> {
|
implements Converter<Map<String, Object>, Map<String, Object>> {
|
||||||
|
|
||||||
private static final Converter<Object, Collection<String>> AUDIENCE_CONVERTER = new AudienceConverter();
|
private static final Converter<Object, Collection<String>> AUDIENCE_CONVERTER = new AudienceConverter();
|
||||||
private static final Converter<Object, URL> ISSUER_CONVERTER = new IssuerConverter();
|
private static final Converter<Object, String> ISSUER_CONVERTER = new IssuerConverter();
|
||||||
private static final Converter<Object, String> STRING_CONVERTER = new StringConverter();
|
private static final Converter<Object, String> STRING_CONVERTER = new StringConverter();
|
||||||
private static final Converter<Object, Instant> TEMPORAL_CONVERTER = new InstantConverter();
|
private static final Converter<Object, Instant> TEMPORAL_CONVERTER = new InstantConverter();
|
||||||
|
|
||||||
@ -157,39 +156,27 @@ public final class MappedJwtClaimSetConverter
|
|||||||
* Coerces an <a target="_blank" href="https://tools.ietf.org/html/rfc7519#section-4.1.1">Issuer</a> claim
|
* Coerces an <a target="_blank" href="https://tools.ietf.org/html/rfc7519#section-4.1.1">Issuer</a> claim
|
||||||
* into a {@link URL}, ignoring null values, and throwing an error if its coercion efforts fail.
|
* into a {@link URL}, ignoring null values, and throwing an error if its coercion efforts fail.
|
||||||
*/
|
*/
|
||||||
private static class IssuerConverter implements Converter<Object, URL> {
|
private static class IssuerConverter implements Converter<Object, String> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL convert(Object source) {
|
public String convert(Object source) {
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source instanceof URL) {
|
if (source instanceof URL) {
|
||||||
return (URL) source;
|
return ((URL) source).toExternalForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source instanceof URI) {
|
if (source instanceof String && ((String) source).contains(":")) {
|
||||||
return toUrl((URI) source);
|
|
||||||
}
|
|
||||||
|
|
||||||
return toUrl(source.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private URL toUrl(URI source) {
|
|
||||||
try {
|
try {
|
||||||
return source.toURL();
|
return URI.create((String) source).toString();
|
||||||
} catch (MalformedURLException e) {
|
} catch (Exception e) {
|
||||||
throw new IllegalStateException("Could not coerce " + source + " into a URL", e);
|
throw new IllegalStateException("Could not coerce " + source + " into a URI String", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private URL toUrl(String source) {
|
return source.toString();
|
||||||
try {
|
|
||||||
return new URL(source);
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
throw new IllegalStateException("Could not coerce " + source + " into a URL", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,15 +82,24 @@ public class JwtIssuerValidatorTests {
|
|||||||
assertThat(result.getErrors()).isNotEmpty();
|
assertThat(result.getErrors()).isNotEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gh-6073
|
||||||
@Test
|
@Test
|
||||||
public void validateWhenJwtIsNullThenThrowsIllegalArgumentException() {
|
public void validateWhenIssuerMatchesAndIsNotAUriThenReturnsSuccess() {
|
||||||
assertThatCode(() -> this.validator.validate(null))
|
Jwt jwt = new Jwt(
|
||||||
.isInstanceOf(IllegalArgumentException.class);
|
MOCK_TOKEN,
|
||||||
|
MOCK_ISSUED_AT,
|
||||||
|
MOCK_EXPIRES_AT,
|
||||||
|
MOCK_HEADERS,
|
||||||
|
Collections.singletonMap(JwtClaimNames.ISS, "issuer"));
|
||||||
|
JwtIssuerValidator validator = new JwtIssuerValidator("issuer");
|
||||||
|
|
||||||
|
assertThat(validator.validate(jwt))
|
||||||
|
.isEqualTo(OAuth2TokenValidatorResult.success());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenMalformedIssuerIsGivenThenThrowsIllegalArgumentException() {
|
public void validateWhenJwtIsNullThenThrowsIllegalArgumentException() {
|
||||||
assertThatCode(() -> new JwtIssuerValidator("issuer"))
|
assertThatCode(() -> this.validator.validate(null))
|
||||||
.isInstanceOf(IllegalArgumentException.class);
|
.isInstanceOf(IllegalArgumentException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ public class MappedJwtClaimSetConverterTests {
|
|||||||
assertThat(target.get(JwtClaimNames.AUD)).isEqualTo(Arrays.asList("audience"));
|
assertThat(target.get(JwtClaimNames.AUD)).isEqualTo(Arrays.asList("audience"));
|
||||||
assertThat(target.get(JwtClaimNames.EXP)).isEqualTo(Instant.ofEpochSecond(2000000000L));
|
assertThat(target.get(JwtClaimNames.EXP)).isEqualTo(Instant.ofEpochSecond(2000000000L));
|
||||||
assertThat(target.get(JwtClaimNames.IAT)).isEqualTo(Instant.ofEpochSecond(1000000000L));
|
assertThat(target.get(JwtClaimNames.IAT)).isEqualTo(Instant.ofEpochSecond(1000000000L));
|
||||||
assertThat(target.get(JwtClaimNames.ISS)).isEqualTo(new URL("https://any.url"));
|
assertThat(target.get(JwtClaimNames.ISS)).isEqualTo("https://any.url");
|
||||||
assertThat(target.get(JwtClaimNames.NBF)).isEqualTo(Instant.ofEpochSecond(1000000000L));
|
assertThat(target.get(JwtClaimNames.NBF)).isEqualTo(Instant.ofEpochSecond(1000000000L));
|
||||||
assertThat(target.get(JwtClaimNames.SUB)).isEqualTo("1234");
|
assertThat(target.get(JwtClaimNames.SUB)).isEqualTo("1234");
|
||||||
}
|
}
|
||||||
@ -135,7 +135,7 @@ public class MappedJwtClaimSetConverterTests {
|
|||||||
assertThat(target.get(JwtClaimNames.AUD)).isEqualTo(Arrays.asList("audience"));
|
assertThat(target.get(JwtClaimNames.AUD)).isEqualTo(Arrays.asList("audience"));
|
||||||
assertThat(target.get(JwtClaimNames.EXP)).isEqualTo(Instant.ofEpochSecond(2000000000L));
|
assertThat(target.get(JwtClaimNames.EXP)).isEqualTo(Instant.ofEpochSecond(2000000000L));
|
||||||
assertThat(target.get(JwtClaimNames.IAT)).isEqualTo(Instant.ofEpochSecond(1000000000L));
|
assertThat(target.get(JwtClaimNames.IAT)).isEqualTo(Instant.ofEpochSecond(1000000000L));
|
||||||
assertThat(target.get(JwtClaimNames.ISS)).isEqualTo(new URL("https://any.url"));
|
assertThat(target.get(JwtClaimNames.ISS)).isEqualTo("https://any.url");
|
||||||
assertThat(target.get(JwtClaimNames.NBF)).isEqualTo(Instant.ofEpochSecond(1000000000L));
|
assertThat(target.get(JwtClaimNames.NBF)).isEqualTo(Instant.ofEpochSecond(1000000000L));
|
||||||
assertThat(target.get(JwtClaimNames.SUB)).isEqualTo("1234");
|
assertThat(target.get(JwtClaimNames.SUB)).isEqualTo("1234");
|
||||||
}
|
}
|
||||||
@ -196,7 +196,7 @@ public class MappedJwtClaimSetConverterTests {
|
|||||||
MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter
|
MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter
|
||||||
.withDefaults(Collections.emptyMap());
|
.withDefaults(Collections.emptyMap());
|
||||||
|
|
||||||
Map<String, Object> badIssuer = Collections.singletonMap(JwtClaimNames.ISS, "badly-formed-iss");
|
Map<String, Object> badIssuer = Collections.singletonMap(JwtClaimNames.ISS, "https://badly formed iss");
|
||||||
assertThatCode(() -> converter.convert(badIssuer)).isInstanceOf(IllegalStateException.class);
|
assertThatCode(() -> converter.convert(badIssuer)).isInstanceOf(IllegalStateException.class);
|
||||||
|
|
||||||
Map<String, Object> badIssuedAt = Collections.singletonMap(JwtClaimNames.IAT, "badly-formed-iat");
|
Map<String, Object> badIssuedAt = Collections.singletonMap(JwtClaimNames.IAT, "badly-formed-iat");
|
||||||
@ -209,6 +209,28 @@ public class MappedJwtClaimSetConverterTests {
|
|||||||
assertThatCode(() -> converter.convert(badNotBefore)).isInstanceOf(IllegalStateException.class);
|
assertThatCode(() -> converter.convert(badNotBefore)).isInstanceOf(IllegalStateException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gh-6073
|
||||||
|
@Test
|
||||||
|
public void convertWhenIssuerIsNotAUriThenConvertsToString() {
|
||||||
|
MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter
|
||||||
|
.withDefaults(Collections.emptyMap());
|
||||||
|
|
||||||
|
Map<String, Object> nonUriIssuer = Collections.singletonMap(JwtClaimNames.ISS, "issuer");
|
||||||
|
Map<String, Object> target = converter.convert(nonUriIssuer);
|
||||||
|
assertThat(target.get(JwtClaimNames.ISS)).isEqualTo("issuer");
|
||||||
|
}
|
||||||
|
|
||||||
|
// gh-6073
|
||||||
|
@Test
|
||||||
|
public void convertWhenIssuerIsOfTypeURLThenConvertsToString() throws Exception {
|
||||||
|
MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter
|
||||||
|
.withDefaults(Collections.emptyMap());
|
||||||
|
|
||||||
|
Map<String, Object> issuer = Collections.singletonMap(JwtClaimNames.ISS, new URL("https://issuer"));
|
||||||
|
Map<String, Object> target = converter.convert(issuer);
|
||||||
|
assertThat(target.get(JwtClaimNames.ISS)).isEqualTo("https://issuer");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructWhenAnyParameterIsNullThenIllegalArgumentException() {
|
public void constructWhenAnyParameterIsNullThenIllegalArgumentException() {
|
||||||
assertThatCode(() -> new MappedJwtClaimSetConverter(null))
|
assertThatCode(() -> new MappedJwtClaimSetConverter(null))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user