Jwt Claim Validation
This introduces OAuth2TokenValidator which allows the customization of validation steps that need to be performing when decoding a string token to a Jwt. At this point, two validators, JwtTimestampValidator and JwtIssuerValidator, are available for use. Fixes: gh-5133
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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
|
||||
*
|
||||
* http://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.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A composite validator
|
||||
*
|
||||
* @param <T> the type of {@link AbstractOAuth2Token} this validator validates
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 5.1
|
||||
*/
|
||||
public final class DelegatingOAuth2TokenValidator<T extends AbstractOAuth2Token>
|
||||
implements OAuth2TokenValidator<T> {
|
||||
|
||||
private final Collection<OAuth2TokenValidator<T>> tokenValidators;
|
||||
|
||||
public DelegatingOAuth2TokenValidator(Collection<OAuth2TokenValidator<T>> tokenValidators) {
|
||||
Assert.notNull(tokenValidators, "tokenValidators cannot be null");
|
||||
|
||||
this.tokenValidators = new ArrayList<>(tokenValidators);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public OAuth2TokenValidatorResult validate(T token) {
|
||||
Collection<OAuth2Error> errors = new ArrayList<>();
|
||||
|
||||
for ( OAuth2TokenValidator<T> validator : this.tokenValidators) {
|
||||
errors.addAll(validator.validate(token).getErrors());
|
||||
}
|
||||
|
||||
return OAuth2TokenValidatorResult.failure(errors);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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
|
||||
*
|
||||
* http://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.core;
|
||||
|
||||
/**
|
||||
* Implementations of this interface are responsible for "verifying"
|
||||
* the validity and/or constraints of the attributes contained in an OAuth 2.0 Token.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Josh Cummings
|
||||
* @since 5.1
|
||||
*/
|
||||
public interface OAuth2TokenValidator<T extends AbstractOAuth2Token> {
|
||||
|
||||
/**
|
||||
* Verify the validity and/or constraints of the provided OAuth 2.0 Token.
|
||||
*
|
||||
* @param token an OAuth 2.0 token
|
||||
* @return OAuth2TokenValidationResult the success or failure detail of the validation
|
||||
*/
|
||||
OAuth2TokenValidatorResult validate(T token);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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
|
||||
*
|
||||
* http://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.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A result emitted from an {@link OAuth2TokenValidator} validation attempt
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 5.1
|
||||
*/
|
||||
public final class OAuth2TokenValidatorResult {
|
||||
static final OAuth2TokenValidatorResult NO_ERRORS = new OAuth2TokenValidatorResult(Collections.emptyList());
|
||||
|
||||
private final Collection<OAuth2Error> errors;
|
||||
|
||||
private OAuth2TokenValidatorResult(Collection<OAuth2Error> errors) {
|
||||
Assert.notNull(errors, "errors cannot be null");
|
||||
this.errors = new ArrayList<>(errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Say whether this result indicates success
|
||||
*
|
||||
* @return whether this result has errors
|
||||
*/
|
||||
public boolean hasErrors() {
|
||||
return !this.errors.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return error details regarding the validation attempt
|
||||
*
|
||||
* @return the collection of results in this result, if any; returns an empty list otherwise
|
||||
*/
|
||||
public Collection<OAuth2Error> getErrors() {
|
||||
return this.errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a successful {@link OAuth2TokenValidatorResult}
|
||||
*
|
||||
* @return an {@link OAuth2TokenValidatorResult} with no errors
|
||||
*/
|
||||
public static OAuth2TokenValidatorResult success() {
|
||||
return NO_ERRORS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a failure {@link OAuth2TokenValidatorResult} with the provided detail
|
||||
*
|
||||
* @param errors the list of errors
|
||||
* @return an {@link OAuth2TokenValidatorResult} with the errors specified
|
||||
*/
|
||||
public static OAuth2TokenValidatorResult failure(OAuth2Error... errors) {
|
||||
return failure(Arrays.asList(errors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a failure {@link OAuth2TokenValidatorResult} with the provided detail
|
||||
*
|
||||
* @param errors the list of errors
|
||||
* @return an {@link OAuth2TokenValidatorResult} with the errors specified
|
||||
*/
|
||||
public static OAuth2TokenValidatorResult failure(Collection<OAuth2Error> errors) {
|
||||
if (errors.isEmpty()) {
|
||||
return NO_ERRORS;
|
||||
}
|
||||
|
||||
return new OAuth2TokenValidatorResult(errors);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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
|
||||
*
|
||||
* http://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.core;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
|
||||
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for verifying {@link DelegatingOAuth2TokenValidator}
|
||||
*
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class DelegatingOAuth2TokenValidatorTests {
|
||||
private static final OAuth2Error DETAIL = new OAuth2Error(
|
||||
"error", "description", "uri");
|
||||
|
||||
@Test
|
||||
public void validateWhenNoValidatorsConfiguredThenReturnsSuccessfulResult() {
|
||||
DelegatingOAuth2TokenValidator<AbstractOAuth2Token> tokenValidator =
|
||||
new DelegatingOAuth2TokenValidator<>(Collections.emptyList());
|
||||
AbstractOAuth2Token token = mock(AbstractOAuth2Token.class);
|
||||
|
||||
assertThat(tokenValidator.validate(token).hasErrors()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateWhenAnyValidatorFailsThenReturnsFailureResultContainingDetailFromFailingValidator() {
|
||||
OAuth2TokenValidator<AbstractOAuth2Token> success = mock(OAuth2TokenValidator.class);
|
||||
OAuth2TokenValidator<AbstractOAuth2Token> failure = mock(OAuth2TokenValidator.class);
|
||||
|
||||
when(success.validate(any(AbstractOAuth2Token.class)))
|
||||
.thenReturn(OAuth2TokenValidatorResult.success());
|
||||
when(failure.validate(any(AbstractOAuth2Token.class)))
|
||||
.thenReturn(OAuth2TokenValidatorResult.failure(DETAIL));
|
||||
|
||||
DelegatingOAuth2TokenValidator<AbstractOAuth2Token> tokenValidator =
|
||||
new DelegatingOAuth2TokenValidator<>(Arrays.asList(success, failure));
|
||||
AbstractOAuth2Token token = mock(AbstractOAuth2Token.class);
|
||||
|
||||
OAuth2TokenValidatorResult result =
|
||||
tokenValidator.validate(token);
|
||||
|
||||
assertThat(result.hasErrors()).isTrue();
|
||||
assertThat(result.getErrors()).containsExactly(DETAIL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateWhenMultipleValidatorsFailThenReturnsFailureResultContainingAllDetails() {
|
||||
OAuth2TokenValidator<AbstractOAuth2Token> firstFailure = mock(OAuth2TokenValidator.class);
|
||||
OAuth2TokenValidator<AbstractOAuth2Token> secondFailure = mock(OAuth2TokenValidator.class);
|
||||
|
||||
OAuth2Error otherDetail = new OAuth2Error("another-error");
|
||||
|
||||
when(firstFailure.validate(any(AbstractOAuth2Token.class)))
|
||||
.thenReturn(OAuth2TokenValidatorResult.failure(DETAIL));
|
||||
when(secondFailure.validate(any(AbstractOAuth2Token.class)))
|
||||
.thenReturn(OAuth2TokenValidatorResult.failure(otherDetail));
|
||||
|
||||
DelegatingOAuth2TokenValidator<AbstractOAuth2Token> tokenValidator =
|
||||
new DelegatingOAuth2TokenValidator<>(Arrays.asList(firstFailure, secondFailure));
|
||||
AbstractOAuth2Token token = mock(AbstractOAuth2Token.class);
|
||||
|
||||
OAuth2TokenValidatorResult result =
|
||||
tokenValidator.validate(token);
|
||||
|
||||
assertThat(result.hasErrors()).isTrue();
|
||||
assertThat(result.getErrors()).containsExactly(DETAIL, otherDetail);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateWhenAllValidatorsSucceedThenReturnsSuccessfulResult() {
|
||||
OAuth2TokenValidator<AbstractOAuth2Token> firstSuccess = mock(OAuth2TokenValidator.class);
|
||||
OAuth2TokenValidator<AbstractOAuth2Token> secondSuccess = mock(OAuth2TokenValidator.class);
|
||||
|
||||
when(firstSuccess.validate(any(AbstractOAuth2Token.class)))
|
||||
.thenReturn(OAuth2TokenValidatorResult.success());
|
||||
when(secondSuccess.validate(any(AbstractOAuth2Token.class)))
|
||||
.thenReturn(OAuth2TokenValidatorResult.success());
|
||||
|
||||
DelegatingOAuth2TokenValidator<AbstractOAuth2Token> tokenValidator =
|
||||
new DelegatingOAuth2TokenValidator<>(Arrays.asList(firstSuccess, secondSuccess));
|
||||
AbstractOAuth2Token token = mock(AbstractOAuth2Token.class);
|
||||
|
||||
OAuth2TokenValidatorResult result =
|
||||
tokenValidator.validate(token);
|
||||
|
||||
assertThat(result.hasErrors()).isFalse();
|
||||
assertThat(result.getErrors()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenInvokedWithNullValidatorListThenThrowsIllegalArgumentException() {
|
||||
assertThatCode(() -> new DelegatingOAuth2TokenValidator<>(null))
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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
|
||||
*
|
||||
* http://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.core;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for verifying {@link OAuth2TokenValidatorResult}
|
||||
*
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class OAuth2TokenValidatorResultTests {
|
||||
private static final OAuth2Error DETAIL = new OAuth2Error(
|
||||
"error", "description", "uri");
|
||||
|
||||
@Test
|
||||
public void successWhenInvokedThenReturnsSuccessfulResult() {
|
||||
OAuth2TokenValidatorResult success = OAuth2TokenValidatorResult.success();
|
||||
assertThat(success.hasErrors()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failureWhenInvokedWithDetailReturnsFailureResultIncludingDetail() {
|
||||
OAuth2TokenValidatorResult failure = OAuth2TokenValidatorResult.failure(DETAIL);
|
||||
|
||||
assertThat(failure.hasErrors()).isTrue();
|
||||
assertThat(failure.getErrors()).containsExactly(DETAIL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failureWhenInvokedWithMultipleDetailsReturnsFailureResultIncludingAll() {
|
||||
OAuth2TokenValidatorResult failure = OAuth2TokenValidatorResult.failure(DETAIL, DETAIL);
|
||||
|
||||
assertThat(failure.hasErrors()).isTrue();
|
||||
assertThat(failure.getErrors()).containsExactly(DETAIL, DETAIL);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user