|
|
|
@ -26,6 +26,7 @@ import java.security.Signature;
|
|
|
|
|
import java.security.spec.InvalidKeySpecException;
|
|
|
|
|
import java.security.spec.PKCS8EncodedKeySpec;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.LinkedHashMap;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
import org.apache.commons.codec.binary.Base64;
|
|
|
|
@ -35,10 +36,12 @@ import org.mockito.Mock;
|
|
|
|
|
import org.mockito.MockitoAnnotations;
|
|
|
|
|
import reactor.core.publisher.Mono;
|
|
|
|
|
import reactor.test.StepVerifier;
|
|
|
|
|
import reactor.test.publisher.PublisherProbe;
|
|
|
|
|
|
|
|
|
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException;
|
|
|
|
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason;
|
|
|
|
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.Token;
|
|
|
|
|
import org.springframework.test.util.ReflectionTestUtils;
|
|
|
|
|
import org.springframework.util.Base64Utils;
|
|
|
|
|
import org.springframework.util.StreamUtils;
|
|
|
|
|
|
|
|
|
@ -77,26 +80,84 @@ public class ReactiveTokenValidatorTests {
|
|
|
|
|
+ "r3F7aM9YpErzeYLrl0GhQr9BVJxOvXcVd4kmY+XkiCcrkyS1cnghnllh+LCwQu1s\n"
|
|
|
|
|
+ "YwIDAQAB\n-----END PUBLIC KEY-----";
|
|
|
|
|
|
|
|
|
|
private static final Map<String, String> INVALID_KEYS = Collections
|
|
|
|
|
.singletonMap("invalid-key", INVALID_KEY);
|
|
|
|
|
private static final Map<String, String> INVALID_KEYS = new LinkedHashMap<>();
|
|
|
|
|
|
|
|
|
|
private static final Map<String, String> VALID_KEYS = Collections
|
|
|
|
|
.singletonMap("valid-key", VALID_KEY);
|
|
|
|
|
private static final Map<String, String> VALID_KEYS = new LinkedHashMap<>();
|
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
|
public void setup() throws Exception {
|
|
|
|
|
MockitoAnnotations.initMocks(this);
|
|
|
|
|
VALID_KEYS.put("valid-key", VALID_KEY);
|
|
|
|
|
INVALID_KEYS.put("invalid-key", INVALID_KEY);
|
|
|
|
|
this.tokenValidator = new ReactiveTokenValidator(this.securityService);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void validateTokenWhenKidValidationFailsShouldThrowException()
|
|
|
|
|
throws Exception {
|
|
|
|
|
given(this.securityService.fetchTokenKeys()).willReturn(Mono.just(INVALID_KEYS));
|
|
|
|
|
public void validateTokenWhenKidValidationFailsTwiceShouldThrowException() throws Exception {
|
|
|
|
|
PublisherProbe<Map<String, String>> fetchTokenKeys = PublisherProbe.of(Mono.just(VALID_KEYS));
|
|
|
|
|
ReflectionTestUtils.setField(this.tokenValidator, "cachedTokenKeys", VALID_KEYS);
|
|
|
|
|
given(this.securityService.fetchTokenKeys()).willReturn(fetchTokenKeys.mono());
|
|
|
|
|
given(this.securityService.getUaaUrl())
|
|
|
|
|
.willReturn(Mono.just("http://localhost:8080/uaa"));
|
|
|
|
|
String header = "{\"alg\": \"RS256\", \"kid\": \"invalid-key\",\"typ\": \"JWT\"}";
|
|
|
|
|
String claims = "{\"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}";
|
|
|
|
|
StepVerifier
|
|
|
|
|
.create(this.tokenValidator.validate(
|
|
|
|
|
new Token(getSignedToken(header.getBytes(), claims.getBytes()))))
|
|
|
|
|
.consumeErrorWith((ex) -> {
|
|
|
|
|
assertThat(ex).isExactlyInstanceOf(
|
|
|
|
|
CloudFoundryAuthorizationException.class);
|
|
|
|
|
assertThat(((CloudFoundryAuthorizationException) ex).getReason())
|
|
|
|
|
.isEqualTo(Reason.INVALID_KEY_ID);
|
|
|
|
|
}).verify();
|
|
|
|
|
Object cachedTokenKeys = ReflectionTestUtils.getField(this.tokenValidator, "cachedTokenKeys");
|
|
|
|
|
assertThat(cachedTokenKeys).isEqualTo(VALID_KEYS);
|
|
|
|
|
fetchTokenKeys.assertWasSubscribed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void validateTokenWhenKidValidationSucceedsInTheSecondAttempt() throws Exception {
|
|
|
|
|
PublisherProbe<Map<String, String>> fetchTokenKeys = PublisherProbe.of(Mono.just(VALID_KEYS));
|
|
|
|
|
ReflectionTestUtils.setField(this.tokenValidator, "cachedTokenKeys", INVALID_KEYS);
|
|
|
|
|
given(this.securityService.fetchTokenKeys()).willReturn(fetchTokenKeys.mono());
|
|
|
|
|
given(this.securityService.getUaaUrl())
|
|
|
|
|
.willReturn(Mono.just("http://localhost:8080/uaa"));
|
|
|
|
|
String header = "{\"alg\": \"RS256\", \"kid\": \"valid-key\",\"typ\": \"JWT\"}";
|
|
|
|
|
String claims = "{\"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}";
|
|
|
|
|
StepVerifier
|
|
|
|
|
.create(this.tokenValidator.validate(
|
|
|
|
|
new Token(getSignedToken(header.getBytes(), claims.getBytes()))))
|
|
|
|
|
.verifyComplete();
|
|
|
|
|
Object cachedTokenKeys = ReflectionTestUtils.getField(this.tokenValidator, "cachedTokenKeys");
|
|
|
|
|
assertThat(cachedTokenKeys).isEqualTo(VALID_KEYS);
|
|
|
|
|
fetchTokenKeys.assertWasSubscribed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void validateTokenWhenCacheIsEmptyShouldFetchTokenKeys() throws Exception {
|
|
|
|
|
PublisherProbe<Map<String, String>> fetchTokenKeys = PublisherProbe.of(Mono.just(VALID_KEYS));
|
|
|
|
|
given(this.securityService.fetchTokenKeys()).willReturn(fetchTokenKeys.mono());
|
|
|
|
|
given(this.securityService.getUaaUrl())
|
|
|
|
|
.willReturn(Mono.just("http://localhost:8080/uaa"));
|
|
|
|
|
String header = "{\"alg\": \"RS256\", \"kid\": \"valid-key\",\"typ\": \"JWT\"}";
|
|
|
|
|
String claims = "{\"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}";
|
|
|
|
|
StepVerifier
|
|
|
|
|
.create(this.tokenValidator.validate(
|
|
|
|
|
new Token(getSignedToken(header.getBytes(), claims.getBytes()))))
|
|
|
|
|
.verifyComplete();
|
|
|
|
|
Object cachedTokenKeys = ReflectionTestUtils.getField(this.tokenValidator, "cachedTokenKeys");
|
|
|
|
|
assertThat(cachedTokenKeys).isEqualTo(VALID_KEYS);
|
|
|
|
|
fetchTokenKeys.assertWasSubscribed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void validateTokenWhenCacheEmptyAndInvalidKeyShouldThrowException() throws Exception {
|
|
|
|
|
PublisherProbe<Map<String, String>> fetchTokenKeys = PublisherProbe.of(Mono.just(VALID_KEYS));
|
|
|
|
|
given(this.securityService.fetchTokenKeys()).willReturn(fetchTokenKeys.mono());
|
|
|
|
|
given(this.securityService.getUaaUrl())
|
|
|
|
|
.willReturn(Mono.just("http://localhost:8080/uaa"));
|
|
|
|
|
String header = "{\"alg\": \"RS256\", \"kid\": \"invalid-key\",\"typ\": \"JWT\"}";
|
|
|
|
|
String claims = "{\"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}";
|
|
|
|
|
StepVerifier
|
|
|
|
|
.create(this.tokenValidator.validate(
|
|
|
|
|
new Token(getSignedToken(header.getBytes(), claims.getBytes()))))
|
|
|
|
@ -106,19 +167,25 @@ public class ReactiveTokenValidatorTests {
|
|
|
|
|
assertThat(((CloudFoundryAuthorizationException) ex).getReason())
|
|
|
|
|
.isEqualTo(Reason.INVALID_KEY_ID);
|
|
|
|
|
}).verify();
|
|
|
|
|
Object cachedTokenKeys = ReflectionTestUtils.getField(this.tokenValidator, "cachedTokenKeys");
|
|
|
|
|
assertThat(cachedTokenKeys).isEqualTo(VALID_KEYS);
|
|
|
|
|
fetchTokenKeys.assertWasSubscribed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void validateTokenWhenKidValidationSucceeds() throws Exception {
|
|
|
|
|
given(this.securityService.fetchTokenKeys()).willReturn(Mono.just(VALID_KEYS));
|
|
|
|
|
public void validateTokenWhenCacheValidShouldNotFetchTokenKeys() throws Exception {
|
|
|
|
|
PublisherProbe<Map<String, String>> fetchTokenKeys = PublisherProbe.empty();
|
|
|
|
|
ReflectionTestUtils.setField(this.tokenValidator, "cachedTokenKeys", VALID_KEYS);
|
|
|
|
|
given(this.securityService.fetchTokenKeys()).willReturn(fetchTokenKeys.mono());
|
|
|
|
|
given(this.securityService.getUaaUrl())
|
|
|
|
|
.willReturn(Mono.just("http://localhost:8080/uaa"));
|
|
|
|
|
String header = "{ \"alg\": \"RS256\", \"kid\": \"valid-key\",\"typ\": \"JWT\"}";
|
|
|
|
|
String claims = "{ \"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}";
|
|
|
|
|
String header = "{\"alg\": \"RS256\", \"kid\": \"valid-key\",\"typ\": \"JWT\"}";
|
|
|
|
|
String claims = "{\"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}";
|
|
|
|
|
StepVerifier
|
|
|
|
|
.create(this.tokenValidator.validate(
|
|
|
|
|
new Token(getSignedToken(header.getBytes(), claims.getBytes()))))
|
|
|
|
|
.verifyComplete();
|
|
|
|
|
fetchTokenKeys.assertWasNotSubscribed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|