diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtClaimValidator.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtClaimValidator.java new file mode 100644 index 00000000000..f5cf23b4638 --- /dev/null +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtClaimValidator.java @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2020 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.oauth2.jwt; + +import org.springframework.security.oauth2.core.OAuth2Error; +import org.springframework.security.oauth2.core.OAuth2ErrorCodes; +import org.springframework.security.oauth2.core.OAuth2TokenValidator; +import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; +import org.springframework.util.Assert; + +import java.util.function.Predicate; + +/** + * Validates a claim in a {@link Jwt} against a provided {@link java.util.function.Predicate} + * + * @author Zeeshan Adnan + * @since 5.3 + */ +public final class JwtClaimValidator implements OAuth2TokenValidator { + + private final String claim; + private final Predicate test; + private final OAuth2Error error; + + /** + * Constructs a {@link JwtClaimValidator} using the provided parameters + * + * @param claim - is the name of the claim in {@link Jwt} to validate. + * @param test - is the predicate function for the claim to test against. + */ + public JwtClaimValidator(String claim, Predicate test) { + Assert.notNull(claim, "claim can not be null"); + Assert.notNull(test, "test can not be null"); + this.claim = claim; + this.test = test; + this.error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, + "The " + this.claim + " claim is not valid", + "https://tools.ietf.org/html/rfc6750#section-3.1"); + } + + /** + * {@inheritDoc} + */ + @Override + public OAuth2TokenValidatorResult validate(Jwt token) { + Assert.notNull(token, "token cannot be null"); + T claimValue = token.getClaim(this.claim); + if (test.test(claimValue)) { + return OAuth2TokenValidatorResult.success(); + } else { + return OAuth2TokenValidatorResult.failure(error); + } + } +} diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtIssuerValidator.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtIssuerValidator.java index 49a5845fec3..da9beacce8d 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtIssuerValidator.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtIssuerValidator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -15,12 +15,12 @@ */ package org.springframework.security.oauth2.jwt; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; import org.springframework.util.Assert; +import static org.springframework.security.oauth2.jwt.JwtClaimNames.ISS; + /** * Validates the "iss" claim in a {@link Jwt}, that is matches a configured value * @@ -28,13 +28,8 @@ * @since 5.1 */ public final class JwtIssuerValidator implements OAuth2TokenValidator { - private static OAuth2Error INVALID_ISSUER = - new OAuth2Error( - OAuth2ErrorCodes.INVALID_REQUEST, - "This iss claim is not equal to the configured issuer", - "https://tools.ietf.org/html/rfc6750#section-3.1"); - private final String issuer; + private final JwtClaimValidator validator; /** * Constructs a {@link JwtIssuerValidator} using the provided parameters @@ -43,7 +38,7 @@ public final class JwtIssuerValidator implements OAuth2TokenValidator { */ public JwtIssuerValidator(String issuer) { Assert.notNull(issuer, "issuer cannot be null"); - this.issuer = issuer; + this.validator = new JwtClaimValidator(ISS, issuer::equals); } /** @@ -52,12 +47,6 @@ public JwtIssuerValidator(String issuer) { @Override public OAuth2TokenValidatorResult validate(Jwt token) { Assert.notNull(token, "token cannot be null"); - - String tokenIssuer = token.getClaimAsString(JwtClaimNames.ISS); - if (this.issuer.equals(tokenIssuer)) { - return OAuth2TokenValidatorResult.success(); - } else { - return OAuth2TokenValidatorResult.failure(INVALID_ISSUER); - } + return this.validator.validate(token); } } diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtClaimValidatorTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtClaimValidatorTests.java new file mode 100644 index 00000000000..968af2a8aba --- /dev/null +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtClaimValidatorTests.java @@ -0,0 +1,69 @@ +/* + * Copyright 2002-2020 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.oauth2.jwt; + +import org.junit.Test; +import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; + +import java.util.function.Predicate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.security.oauth2.jwt.JwtClaimNames.ISS; +import static org.springframework.security.oauth2.jwt.TestJwts.jwt; + +/** + * Tests for {@link JwtClaimValidator}. + * + * @author Zeeshan Adnan + */ +public class JwtClaimValidatorTests { + + private static final Predicate test = claim -> claim.equals("http://test"); + private final JwtClaimValidator validator = new JwtClaimValidator<>(ISS, test); + + @Test + public void validateWhenClaimPassesTheTestThenReturnsSuccess() { + Jwt jwt = jwt().claim(ISS, "http://test").build(); + assertThat(validator.validate(jwt)) + .isEqualTo(OAuth2TokenValidatorResult.success()); + } + + @Test + public void validateWhenClaimFailsTheTestThenReturnsFailure() { + Jwt jwt = jwt().claim(ISS, "http://abc").build(); + assertThat(validator.validate(jwt).getErrors().isEmpty()) + .isFalse(); + } + + @Test + public void validateWhenClaimIsNullThenThrowsIllegalArgumentException() { + assertThatThrownBy(() -> new JwtClaimValidator(null, test)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void validateWhenTestIsNullThenThrowsIllegalArgumentException(){ + assertThatThrownBy(() -> new JwtClaimValidator<>(ISS, null)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void validateWhenJwtIsNullThenThrowsIllegalArgumentException() { + assertThatThrownBy(() -> validator.validate(null)) + .isInstanceOf(IllegalArgumentException.class); + } +}