Polish to Avoid NPE

Issue gh-5648

Co-authored-by: MattyA <mat.auburn@gmail.com>
This commit is contained in:
Josh Cummings 2020-07-30 16:22:49 -06:00
parent 950769fa00
commit f3695932de
No known key found for this signature in database
GPG Key ID: 49EF60DD7FF83443
4 changed files with 80 additions and 29 deletions

View File

@ -22,6 +22,7 @@ import java.net.URL;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import java.text.ParseException; import java.text.ParseException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -55,11 +56,13 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity; import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm; import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@ -167,9 +170,17 @@ public final class NimbusJwtDecoder implements JwtDecoder {
private Jwt validateJwt(Jwt jwt){ private Jwt validateJwt(Jwt jwt){
OAuth2TokenValidatorResult result = this.jwtValidator.validate(jwt); OAuth2TokenValidatorResult result = this.jwtValidator.validate(jwt);
if (result.hasErrors()) { if (result.hasErrors()) {
String description = result.getErrors().iterator().next().getDescription(); Collection<OAuth2Error> errors = result.getErrors();
String validationErrorString = "Unable to validate Jwt";
for (OAuth2Error oAuth2Error : errors) {
if (!StringUtils.isEmpty(oAuth2Error.getDescription())) {
validationErrorString = String.format(
DECODING_ERROR_MESSAGE_TEMPLATE, oAuth2Error.getDescription());
break;
}
}
throw new JwtValidationException( throw new JwtValidationException(
String.format(DECODING_ERROR_MESSAGE_TEMPLATE, description), validationErrorString,
result.getErrors()); result.getErrors());
} }

View File

@ -15,17 +15,6 @@
*/ */
package org.springframework.security.oauth2.jwt; package org.springframework.security.oauth2.jwt;
import java.security.interfaces.RSAPublicKey;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.crypto.SecretKey;
import com.nimbusds.jose.Header; import com.nimbusds.jose.Header;
import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSAlgorithm;
@ -47,17 +36,30 @@ import com.nimbusds.jwt.PlainJWT;
import com.nimbusds.jwt.SignedJWT; import com.nimbusds.jwt.SignedJWT;
import com.nimbusds.jwt.proc.DefaultJWTProcessor; import com.nimbusds.jwt.proc.DefaultJWTProcessor;
import com.nimbusds.jwt.proc.JWTProcessor; import com.nimbusds.jwt.proc.JWTProcessor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm; import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm; import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.crypto.SecretKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
/** /**
* An implementation of a {@link ReactiveJwtDecoder} that &quot;decodes&quot; a * An implementation of a {@link ReactiveJwtDecoder} that &quot;decodes&quot; a
@ -178,10 +180,16 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
private Jwt validateJwt(Jwt jwt) { private Jwt validateJwt(Jwt jwt) {
OAuth2TokenValidatorResult result = this.jwtValidator.validate(jwt); OAuth2TokenValidatorResult result = this.jwtValidator.validate(jwt);
if (result.hasErrors()) { if (result.hasErrors()) {
String message = result.getErrors().iterator().next().getDescription(); Collection<OAuth2Error> errors = result.getErrors();
throw new JwtValidationException(message, result.getErrors()); String validationErrorString = "Unable to validate Jwt";
for (OAuth2Error oAuth2Error : errors) {
if (!StringUtils.isEmpty(oAuth2Error.getDescription())) {
validationErrorString = oAuth2Error.getDescription();
break;
}
}
throw new JwtValidationException(validationErrorString, errors);
} }
return jwt; return jwt;

View File

@ -193,6 +193,22 @@ public class NimbusJwtDecoderTests {
.hasFieldOrPropertyWithValue("errors", Arrays.asList(firstFailure, secondFailure)); .hasFieldOrPropertyWithValue("errors", Arrays.asList(firstFailure, secondFailure));
} }
@Test
public void decodeWhenReadingErrorPickTheFirstErrorMessage() {
OAuth2TokenValidator<Jwt> jwtValidator = mock(OAuth2TokenValidator.class);
this.jwtDecoder.setJwtValidator(jwtValidator);
OAuth2Error errorEmpty = new OAuth2Error("mock-error", "", "mock-uri");
OAuth2Error error = new OAuth2Error("mock-error", "mock-description", "mock-uri");
OAuth2Error error2 = new OAuth2Error("mock-error-second", "mock-description-second", "mock-uri-second");
OAuth2TokenValidatorResult result = OAuth2TokenValidatorResult.failure(errorEmpty, error, error2);
when(jwtValidator.validate(any(Jwt.class))).thenReturn(result);
Assertions.assertThatCode(() -> this.jwtDecoder.decode(SIGNED_JWT))
.isInstanceOf(JwtValidationException.class)
.hasMessageContaining("mock-description");
}
@Test @Test
public void decodeWhenUsingSignedJwtThenReturnsClaimsGivenByClaimSetConverter() { public void decodeWhenUsingSignedJwtThenReturnsClaimsGivenByClaimSetConverter() {
Converter<Map<String, Object>, Map<String, Object>> claimSetConverter = mock(Converter.class); Converter<Map<String, Object>, Map<String, Object>> claimSetConverter = mock(Converter.class);

View File

@ -221,6 +221,22 @@ public class NimbusReactiveJwtDecoderTests {
.hasMessageContaining("mock-description"); .hasMessageContaining("mock-description");
} }
@Test
public void decodeWhenReadingErrorPickTheFirstErrorMessage() {
OAuth2TokenValidator<Jwt> jwtValidator = mock(OAuth2TokenValidator.class);
this.decoder.setJwtValidator(jwtValidator);
OAuth2Error errorEmpty = new OAuth2Error("mock-error", "", "mock-uri");
OAuth2Error error = new OAuth2Error("mock-error", "mock-description", "mock-uri");
OAuth2Error error2 = new OAuth2Error("mock-error-second", "mock-description-second", "mock-uri-second");
OAuth2TokenValidatorResult result = OAuth2TokenValidatorResult.failure(errorEmpty, error, error2);
when(jwtValidator.validate(any(Jwt.class))).thenReturn(result);
assertThatCode(() -> this.decoder.decode(this.messageReadToken).block())
.isInstanceOf(JwtValidationException.class)
.hasMessageContaining("mock-description");
}
@Test @Test
public void decodeWhenUsingSignedJwtThenReturnsClaimsGivenByClaimSetConverter() { public void decodeWhenUsingSignedJwtThenReturnsClaimsGivenByClaimSetConverter() {
Converter<Map<String, Object>, Map<String, Object>> claimSetConverter = mock(Converter.class); Converter<Map<String, Object>, Map<String, Object>> claimSetConverter = mock(Converter.class);