mirror of
https://github.com/spring-projects/spring-security.git
synced 2026-03-31 06:26:24 +00:00
Compare commits
No commits in common. "main" and "7.1.0-M3" have entirely different histories.
1
.github/workflows/auto-merge-dependabot.yml
vendored
1
.github/workflows/auto-merge-dependabot.yml
vendored
@ -5,7 +5,6 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- '*.x'
|
||||
- 'docs-build'
|
||||
|
||||
run-name: Merge Dependabot PR ${{ github.ref_name }}
|
||||
|
||||
|
||||
4
.github/workflows/check-snapshots.yml
vendored
4
.github/workflows/check-snapshots.yml
vendored
@ -14,7 +14,7 @@ permissions:
|
||||
jobs:
|
||||
snapshot-test:
|
||||
name: Test Against Snapshots
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@ -31,6 +31,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Notification
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
|
||||
2
.github/workflows/finalize-release.yml
vendored
2
.github/workflows/finalize-release.yml
vendored
@ -16,7 +16,7 @@ permissions:
|
||||
jobs:
|
||||
perform-release:
|
||||
name: Perform Release
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
|
||||
with:
|
||||
should-perform-release: true
|
||||
project-version: ${{ inputs.version }}
|
||||
|
||||
@ -30,6 +30,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Notification
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
|
||||
6
.github/workflows/pr-build-workflow.yml
vendored
6
.github/workflows/pr-build-workflow.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@c8668747d7c264864c8c7f7026d0d277d14a78dc # v2.0.6
|
||||
uses: spring-io/spring-gradle-build-action@efc55f07f4dfa22f2afd97f9ea1be4212eeed737 # v2.0.5
|
||||
with:
|
||||
java-version: '25'
|
||||
distribution: 'temurin'
|
||||
@ -26,7 +26,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@c8668747d7c264864c8c7f7026d0d277d14a78dc # v2.0.6
|
||||
uses: spring-io/spring-gradle-build-action@efc55f07f4dfa22f2afd97f9ea1be4212eeed737 # v2.0.5
|
||||
with:
|
||||
java-version: '25'
|
||||
distribution: 'temurin'
|
||||
@ -46,6 +46,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Notification
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
|
||||
@ -9,7 +9,7 @@ permissions:
|
||||
jobs:
|
||||
update-scheduled-release-version:
|
||||
name: Update Scheduled Release Version
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
|
||||
secrets: inherit
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
@ -18,6 +18,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Notification
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
@ -20,8 +20,6 @@ import java.io.Serial;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.annotation.SecurityAnnotationScanner;
|
||||
import org.springframework.util.Assert;
|
||||
@ -52,7 +50,7 @@ public class SecurityConfig implements ConfigAttribute {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ConfigAttribute attr) {
|
||||
return this.attrib.equals(attr.getAttribute());
|
||||
}
|
||||
|
||||
@ -114,10 +114,8 @@ public final class DelegatingMethodSecurityMetadataSource extends AbstractMethod
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
if (!(other instanceof DefaultCacheKey otherKey)) {
|
||||
return false;
|
||||
}
|
||||
public boolean equals(Object other) {
|
||||
DefaultCacheKey otherKey = (DefaultCacheKey) other;
|
||||
return (this.method.equals(otherKey.method)
|
||||
&& ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass));
|
||||
}
|
||||
|
||||
@ -265,7 +265,7 @@ public class MapBasedMethodSecurityMetadataSource extends AbstractFallbackMethod
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -145,7 +145,6 @@ public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor
|
||||
.map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Publisher<?>> @Nullable T proceed(final MethodInvocation invocation) {
|
||||
try {
|
||||
return (T) invocation.proceed();
|
||||
|
||||
@ -111,7 +111,6 @@ public class AclEntryAfterInvocationCollectionFilteringProvider extends Abstract
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private Filterer getFilterer(Object returnedObject) {
|
||||
if (returnedObject instanceof Collection) {
|
||||
return new CollectionFilterer((Collection) returnedObject);
|
||||
|
||||
@ -50,7 +50,6 @@ class MessageExpressionConfigAttribute implements ConfigAttribute, EvaluationCon
|
||||
* @param authorizeExpression the {@link Expression} to use. Cannot be null
|
||||
* @param matcher the {@link MessageMatcher} used to match the messages.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
MessageExpressionConfigAttribute(Expression authorizeExpression, MessageMatcher<?> matcher) {
|
||||
Assert.notNull(authorizeExpression, "authorizeExpression cannot be null");
|
||||
Assert.notNull(matcher, "matcher cannot be null");
|
||||
|
||||
@ -41,7 +41,6 @@ public class DefaultWebSecurityExpressionHandler extends AbstractSecurityExpress
|
||||
private String defaultRolePrefix = DEFAULT_ROLE_PREFIX;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected SecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,
|
||||
FilterInvocation fi) {
|
||||
FilterInvocationExpressionRoot root = new FilterInvocationExpressionRoot(() -> authentication, fi);
|
||||
|
||||
@ -29,7 +29,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AuthenticationCredentialsNotFoundEventTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -32,7 +32,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AuthorizationFailureEventTests {
|
||||
|
||||
private final UsernamePasswordAuthenticationToken foo = UsernamePasswordAuthenticationToken.unauthenticated("foo",
|
||||
|
||||
@ -29,7 +29,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AuthorizedEventTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -27,7 +27,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class SecurityConfigTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -32,7 +32,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Luke Taylor
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Jsr250MethodSecurityMetadataSourceTests {
|
||||
|
||||
Jsr250MethodSecurityMetadataSource mds;
|
||||
|
||||
@ -31,7 +31,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Jsr250VoterTests {
|
||||
|
||||
// SEC-1443
|
||||
|
||||
@ -46,7 +46,6 @@ import static org.assertj.core.api.Assertions.fail;
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class SecuredAnnotationSecurityMetadataSourceTests {
|
||||
|
||||
private SecuredAnnotationSecurityMetadataSource mds = new SecuredAnnotationSecurityMetadataSource();
|
||||
|
||||
@ -79,13 +79,11 @@ public class DefaultMethodSecurityExpressionHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setTrustResolverNull() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.handler.setTrustResolver(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void createEvaluationContextCustomTrustResolver() {
|
||||
setupMocks();
|
||||
this.handler.setTrustResolver(this.trustResolver);
|
||||
@ -177,7 +175,7 @@ public class DefaultMethodSecurityExpressionHandlerTests {
|
||||
@Test
|
||||
public void createEvaluationContextSupplierAuthentication() {
|
||||
setupMocks();
|
||||
Supplier<Authentication> mockAuthenticationSupplier = mock();
|
||||
Supplier<Authentication> mockAuthenticationSupplier = mock(Supplier.class);
|
||||
given(mockAuthenticationSupplier.get()).willReturn(this.authentication);
|
||||
EvaluationContext context = this.handler.createEvaluationContext(mockAuthenticationSupplier,
|
||||
this.methodInvocation);
|
||||
|
||||
@ -39,7 +39,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* @since 5.2
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ExpressionBasedPreInvocationAdviceTests {
|
||||
|
||||
@Mock
|
||||
|
||||
@ -34,7 +34,7 @@ import org.springframework.security.util.SimpleMethodInvocation;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
||||
@SuppressWarnings("unchecked")
|
||||
public class MethodExpressionVoterTests {
|
||||
|
||||
private TestingAuthenticationToken joe = new TestingAuthenticationToken("joe", "joespass", "ROLE_blah");
|
||||
|
||||
@ -27,7 +27,6 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.ExpressionUtils;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@ -59,9 +58,7 @@ public class MethodSecurityExpressionRootTests {
|
||||
this.ctx = new StandardEvaluationContext();
|
||||
this.ctx.setRootObject(this.root);
|
||||
this.trustResolver = mock(AuthenticationTrustResolver.class);
|
||||
DefaultAuthorizationManagerFactory<MethodInvocation> authorizationManagerFactory = new DefaultAuthorizationManagerFactory<>();
|
||||
authorizationManagerFactory.setTrustResolver(this.trustResolver);
|
||||
this.root.setAuthorizationManagerFactory(authorizationManagerFactory);
|
||||
this.root.setTrustResolver(this.trustResolver);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -44,7 +44,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
|
||||
private PrePostAnnotationSecurityMetadataSource mds = new PrePostAnnotationSecurityMetadataSource(
|
||||
|
||||
@ -32,7 +32,6 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AbstractSecurityInterceptorTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -38,7 +38,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
||||
@SuppressWarnings("unchecked")
|
||||
public class AfterInvocationProviderManagerTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -34,7 +34,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class InterceptorStatusTokenTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -27,7 +27,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class NullRunAsManagerTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -31,7 +31,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
/**
|
||||
* Tests {@link RunAsImplAuthenticationProvider}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RunAsImplAuthenticationProviderTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -34,7 +34,6 @@ import static org.assertj.core.api.Assertions.fail;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RunAsManagerImplTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -29,7 +29,6 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RunAsUserTokenTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -65,7 +65,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
* @author Ben Alex
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
||||
@SuppressWarnings("unchecked")
|
||||
public class MethodSecurityInterceptorTests {
|
||||
|
||||
private TestingAuthenticationToken token;
|
||||
|
||||
@ -33,7 +33,6 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MethodSecurityMetadataSourceAdvisorTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -62,7 +62,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
* @author Luke Taylor
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AspectJMethodSecurityInterceptorTests {
|
||||
|
||||
private TestingAuthenticationToken token;
|
||||
|
||||
@ -34,7 +34,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Luke Taylor
|
||||
* @since 2.0.4
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MapBasedMethodSecurityMetadataSourceTests {
|
||||
|
||||
private final List<ConfigAttribute> ROLE_A = SecurityConfig.createList("ROLE_A");
|
||||
|
||||
@ -49,7 +49,6 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MethodInvocationPrivilegeEvaluatorTests {
|
||||
|
||||
private TestingAuthenticationToken token;
|
||||
|
||||
@ -36,7 +36,7 @@ import static org.mockito.Mockito.mock;
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public class DelegatingMethodSecurityMetadataSourceTests {
|
||||
|
||||
DelegatingMethodSecurityMetadataSource mds;
|
||||
|
||||
@ -29,7 +29,6 @@ import org.springframework.security.access.intercept.aspectj.MethodInvocationAda
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@SuppressWarnings("deprecation")
|
||||
public class PostInvocationAdviceProviderTests {
|
||||
|
||||
@Mock
|
||||
|
||||
@ -29,7 +29,6 @@ import org.springframework.security.access.intercept.aspectj.MethodInvocationAda
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@SuppressWarnings("deprecation")
|
||||
public class PreInvocationAuthorizationAdviceVoterTests {
|
||||
|
||||
@Mock
|
||||
|
||||
@ -36,7 +36,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
||||
@SuppressWarnings("unchecked")
|
||||
public class AbstractAccessDecisionManagerTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -31,7 +31,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AbstractAclVoterTests {
|
||||
|
||||
private AbstractAclVoter voter = new AbstractAclVoter() {
|
||||
|
||||
@ -40,7 +40,6 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AffirmativeBasedTests {
|
||||
|
||||
private final List<ConfigAttribute> attrs = new ArrayList<>();
|
||||
|
||||
@ -37,7 +37,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AuthenticatedVoterTests {
|
||||
|
||||
private Authentication createAnonymous() {
|
||||
|
||||
@ -35,7 +35,6 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ConsensusBasedTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -32,7 +32,6 @@ import org.springframework.security.core.Authentication;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class DenyAgainVoter implements AccessDecisionVoter<Object> {
|
||||
|
||||
@Override
|
||||
|
||||
@ -34,7 +34,6 @@ import org.springframework.security.core.Authentication;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class DenyVoter implements AccessDecisionVoter<Object> {
|
||||
|
||||
@Override
|
||||
|
||||
@ -25,7 +25,6 @@ import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RoleHierarchyVoterTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -28,7 +28,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RoleVoterTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -35,7 +35,6 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class UnanimousBasedTests {
|
||||
|
||||
private UnanimousBased makeDecisionManager() {
|
||||
|
||||
@ -44,7 +44,7 @@ import static org.mockito.Mockito.verify;
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public class AclEntryAfterInvocationCollectionFilteringProviderTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -48,7 +48,7 @@ import static org.mockito.Mockito.verify;
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public class AclEntryAfterInvocationProviderTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -35,7 +35,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ExpressionBasedMessageSecurityMetadataSourceFactoryTests {
|
||||
|
||||
@Mock
|
||||
@ -58,7 +57,7 @@ public class ExpressionBasedMessageSecurityMetadataSourceFactoryTests {
|
||||
|
||||
MessageSecurityMetadataSource source;
|
||||
|
||||
MessageSecurityExpressionRoot<Object> rootObject;
|
||||
MessageSecurityExpressionRoot rootObject;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
@ -69,7 +68,7 @@ public class ExpressionBasedMessageSecurityMetadataSourceFactoryTests {
|
||||
this.matcherToExpression.put(this.matcher2, this.expression2);
|
||||
this.source = ExpressionBasedMessageSecurityMetadataSourceFactory
|
||||
.createExpressionMessageMetadataSource(this.matcherToExpression);
|
||||
this.rootObject = new MessageSecurityExpressionRoot<>(this.authentication, this.message);
|
||||
this.rootObject = new MessageSecurityExpressionRoot(this.authentication, this.message);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -37,7 +37,6 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MessageExpressionConfigAttributeTests {
|
||||
|
||||
@Mock
|
||||
|
||||
@ -44,7 +44,6 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MessageExpressionVoterTests {
|
||||
|
||||
@Mock
|
||||
@ -77,7 +76,6 @@ public class MessageExpressionVoterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void voteGranted() {
|
||||
given(this.expression.getValue(any(EvaluationContext.class), eq(Boolean.class))).willReturn(true);
|
||||
given(this.matcher.matcher(any())).willCallRealMethod();
|
||||
@ -86,7 +84,6 @@ public class MessageExpressionVoterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void voteDenied() {
|
||||
given(this.expression.getValue(any(EvaluationContext.class), eq(Boolean.class))).willReturn(false);
|
||||
given(this.matcher.matcher(any())).willCallRealMethod();
|
||||
@ -95,7 +92,6 @@ public class MessageExpressionVoterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void voteAbstain() {
|
||||
this.attributes = Arrays.<ConfigAttribute>asList(new SecurityConfig("ROLE_USER"));
|
||||
assertThat(this.voter.vote(this.authentication, this.message, this.attributes))
|
||||
@ -103,13 +99,11 @@ public class MessageExpressionVoterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void supportsObjectClassFalse() {
|
||||
assertThat(this.voter.supports(Object.class)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void supportsMessageClassTrue() {
|
||||
assertThat(this.voter.supports(Message.class)).isTrue();
|
||||
}
|
||||
@ -125,13 +119,11 @@ public class MessageExpressionVoterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setExpressionHandlerNull() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.voter.setExpressionHandler(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void customExpressionHandler() {
|
||||
this.voter.setExpressionHandler(this.expressionHandler);
|
||||
given(this.expressionHandler.createEvaluationContext(this.authentication, this.message))
|
||||
@ -144,7 +136,6 @@ public class MessageExpressionVoterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void postProcessEvaluationContext() {
|
||||
final MessageExpressionConfigAttribute configAttribute = mock(MessageExpressionConfigAttribute.class);
|
||||
this.voter.setExpressionHandler(this.expressionHandler);
|
||||
|
||||
@ -47,7 +47,6 @@ import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willThrow;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ChannelSecurityInterceptorTests {
|
||||
|
||||
@Mock
|
||||
|
||||
@ -36,7 +36,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@SuppressWarnings("deprecation")
|
||||
public class DefaultMessageSecurityMetadataSourceTests {
|
||||
|
||||
@Mock
|
||||
|
||||
@ -43,7 +43,6 @@ import org.springframework.security.web.access.intercept.FilterSecurityIntercept
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class DefaultWebInvocationPrivilegeEvaluatorTests {
|
||||
|
||||
private AccessDecisionManager adm;
|
||||
|
||||
@ -42,7 +42,7 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
||||
@SuppressWarnings("unchecked")
|
||||
public class ChannelDecisionManagerImplTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -39,7 +39,6 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ChannelProcessingFilterTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -35,7 +35,6 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class InsecureChannelProcessorTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -36,7 +36,6 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RetryWithHttpEntryPointTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -33,7 +33,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class RetryWithHttpsEntryPointTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -35,7 +35,6 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class SecureChannelProcessorTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -64,7 +64,6 @@ public class DefaultWebSecurityExpressionHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void expressionPropertiesAreResolvedAgainstAppContextBeans() {
|
||||
StaticApplicationContext appContext = new StaticApplicationContext();
|
||||
RootBeanDefinition bean = new RootBeanDefinition(SecurityConfig.class);
|
||||
@ -79,13 +78,11 @@ public class DefaultWebSecurityExpressionHandlerTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setTrustResolverNull() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.handler.setTrustResolver(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void createEvaluationContextCustomTrustResolver() {
|
||||
this.handler.setTrustResolver(this.trustResolver);
|
||||
Expression expression = this.handler.getExpressionParser().parseExpression("anonymous");
|
||||
|
||||
@ -33,7 +33,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ExpressionBasedFilterInvocationSecurityMetadataSourceTests {
|
||||
|
||||
@Test
|
||||
|
||||
@ -41,7 +41,7 @@ import static org.mockito.Mockito.mock;
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "deprecation" })
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public class WebExpressionVoterTests {
|
||||
|
||||
private Authentication user = new TestingAuthenticationToken("user", "pass", "X");
|
||||
|
||||
@ -40,7 +40,6 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class DefaultFilterInvocationSecurityMetadataSourceTests {
|
||||
|
||||
private DefaultFilterInvocationSecurityMetadataSource fids;
|
||||
|
||||
@ -62,7 +62,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
* @author Luke Taylor
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class FilterSecurityInterceptorTests {
|
||||
|
||||
private AuthenticationManager am;
|
||||
|
||||
@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.security.acls.domain;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
|
||||
/**
|
||||
@ -54,7 +52,7 @@ public abstract class AbstractPermission implements Permission {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(@Nullable Object obj) {
|
||||
public final boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ public class AccessControlEntryImpl implements AccessControlEntry, AuditableAcce
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object arg0) {
|
||||
public boolean equals(Object arg0) {
|
||||
if (!(arg0 instanceof AccessControlEntryImpl)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -278,7 +278,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.security.acls.domain;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.util.Assert;
|
||||
@ -49,7 +47,7 @@ public class GrantedAuthoritySid implements Sid {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object object) {
|
||||
public boolean equals(Object object) {
|
||||
if ((object == null) || !(object instanceof GrantedAuthoritySid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -19,8 +19,6 @@ package org.springframework.security.acls.domain;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@ -99,7 +97,7 @@ public class ObjectIdentityImpl implements ObjectIdentity {
|
||||
* @return <code>true</code> if the presented object matches this object
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || !(obj instanceof ObjectIdentityImpl)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.security.acls.domain;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
@ -48,7 +46,7 @@ public class PrincipalSid implements Sid {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object object) {
|
||||
public boolean equals(Object object) {
|
||||
if ((object == null) || !(object instanceof PrincipalSid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -18,8 +18,6 @@ package org.springframework.security.acls.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents the identity of an individual domain object instance.
|
||||
*
|
||||
@ -42,7 +40,7 @@ public interface ObjectIdentity extends Serializable {
|
||||
* @see Object#equals(Object)
|
||||
*/
|
||||
@Override
|
||||
boolean equals(@Nullable Object obj);
|
||||
boolean equals(Object obj);
|
||||
|
||||
/**
|
||||
* Obtains the actual identifier. This identifier must not be reused to represent
|
||||
|
||||
@ -18,8 +18,6 @@ package org.springframework.security.acls.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A security identity recognised by the ACL system.
|
||||
*
|
||||
@ -42,7 +40,7 @@ public interface Sid extends Serializable {
|
||||
* @return <code>true</code> if the objects are equal, <code>false</code> otherwise
|
||||
*/
|
||||
@Override
|
||||
boolean equals(@Nullable Object obj);
|
||||
boolean equals(Object obj);
|
||||
|
||||
/**
|
||||
* Refer to the <code>java.lang.Object</code> documentation for the interface
|
||||
|
||||
@ -80,7 +80,7 @@ develocity {
|
||||
}
|
||||
|
||||
nohttp {
|
||||
source.exclude "buildSrc/build/**", "**/build/**", "**/target/**", "javascript/.gradle/**", "javascript/package-lock.json", "javascript/node_modules/**", "javascript/build/**", "javascript/dist/**"
|
||||
source.exclude "buildSrc/build/**", "javascript/.gradle/**", "javascript/package-lock.json", "javascript/node_modules/**", "javascript/build/**", "javascript/dist/**"
|
||||
source.builtBy(project(':spring-security-config').tasks.withType(RncToXsd))
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,6 @@ plugins {
|
||||
id "groovy-gradle-plugin"
|
||||
id "java"
|
||||
id "groovy"
|
||||
id "org.jetbrains.dokka" version "2.2.0"
|
||||
}
|
||||
|
||||
java {
|
||||
@ -80,7 +79,6 @@ dependencies {
|
||||
implementation libs.io.spring.javaformat.spring.javaformat.gradle.plugin
|
||||
implementation libs.io.spring.nohttp.nohttp.gradle
|
||||
implementation libs.org.jetbrains.kotlin.kotlin.gradle.plugin
|
||||
implementation libs.org.jetbrains.dokka.dokka.gradle.plugin
|
||||
implementation (libs.net.sourceforge.htmlunit) {
|
||||
exclude group: 'org.eclipse.jetty.websocket', module: 'websocket-client'
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.plugins.PluginManager
|
||||
import org.gradle.api.tasks.Sync
|
||||
import org.gradle.api.tasks.bundling.Zip
|
||||
|
||||
/**
|
||||
@ -19,37 +18,6 @@ public class DocsPlugin implements Plugin<Project> {
|
||||
PluginManager pluginManager = project.getPluginManager();
|
||||
pluginManager.apply(BasePlugin);
|
||||
pluginManager.apply(JavadocApiPlugin);
|
||||
pluginManager.apply("org.jetbrains.dokka");
|
||||
|
||||
project.rootProject.subprojects { subproject ->
|
||||
subproject.pluginManager.withPlugin("security-kotlin") {
|
||||
subproject.pluginManager.apply("org.jetbrains.dokka")
|
||||
configureDokka(subproject)
|
||||
project.dependencies.add("dokka", subproject)
|
||||
}
|
||||
}
|
||||
|
||||
project.extensions.configure("dokka") { dokka ->
|
||||
dokka.moduleName.set(Utils.getProjectName(project) + " Kotlin API")
|
||||
}
|
||||
|
||||
project.tasks.named("dokkaGeneratePublicationHtml").configure { it.dependsOn("api") }
|
||||
|
||||
project.tasks.register("syncAntoraAttachments", Sync) { sync ->
|
||||
sync.group = 'Documentation'
|
||||
sync.description = 'Syncs the Antora attachments'
|
||||
sync.into(project.layout.buildDirectory.dir('generated-antora-resources/modules/ROOT/assets/attachments/api'))
|
||||
sync.from(project.provider({ project.tasks.api.outputs })) { copy ->
|
||||
copy.into('java')
|
||||
}
|
||||
sync.from(project.tasks.named("dokkaGeneratePublicationHtml")) { copy ->
|
||||
copy.into('kotlin')
|
||||
}
|
||||
}
|
||||
|
||||
project.tasks.register("generateAntoraResources") {
|
||||
it.dependsOn 'generateAntoraYml', 'syncAntoraAttachments'
|
||||
}
|
||||
|
||||
Task docsZip = project.tasks.create('docsZip', Zip) {
|
||||
dependsOn 'api'
|
||||
@ -73,16 +41,4 @@ public class DocsPlugin implements Plugin<Project> {
|
||||
}
|
||||
project.tasks.assemble.dependsOn docs
|
||||
}
|
||||
|
||||
void configureDokka(Project project) {
|
||||
project.extensions.configure("dokka") { dokka ->
|
||||
dokka.dokkaSourceSets.configureEach { spec ->
|
||||
spec.suppressedFiles.from(project.fileTree("src/main/java"))
|
||||
spec.externalDocumentationLinks.register("javadoc") {
|
||||
it.url.set(new URI("https://docs.spring.io/${Utils.getProjectName(project)}/reference/${project.rootProject.version}/api/java/"))
|
||||
it.packageListUrl.set(project.layout.buildDirectory.file("api/element-list").get().asFile.toURI())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,12 +21,8 @@ import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.provider.ProviderFactory;
|
||||
import org.gradle.api.specs.Spec;
|
||||
import org.gradle.api.tasks.CacheableTask;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
@ -34,7 +30,6 @@ import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.TaskExecutionException;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.api.tasks.VerificationException;
|
||||
import org.gradle.process.ExecOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@ -49,53 +44,21 @@ public class CheckExpectedBranchVersionPlugin implements Plugin<Project> {
|
||||
TaskProvider<CheckExpectedBranchVersionTask> checkExpectedBranchVersionTask = project.getTasks().register("checkExpectedBranchVersion", CheckExpectedBranchVersionTask.class, (task) -> {
|
||||
task.setGroup("Build");
|
||||
task.setDescription("Check if the project version matches the branch version");
|
||||
task.onlyIf("Property 'skipCheckExpectedBranchVersion' is false or not present", skipPropertyFalseOrNotPresent(project.getProviders()));
|
||||
task.onlyIf("Branch name matches expected version pattern *.x", CheckExpectedBranchVersionPlugin::isVersionBranch);
|
||||
task.onlyIf("skipCheckExpectedBranchVersion property is false or not present", CheckExpectedBranchVersionPlugin::skipPropertyFalseOrNotPresent);
|
||||
task.getVersion().convention(project.provider(() -> project.getVersion().toString()));
|
||||
task.getBranchName().convention(getBranchName(project.getProviders(), project.getLogger()));
|
||||
task.getBranchName().convention(project.getProviders().exec((execSpec) -> execSpec.setCommandLine("git", "symbolic-ref", "--short", "HEAD")).getStandardOutput().getAsText());
|
||||
task.getOutputFile().convention(project.getLayout().getBuildDirectory().file("check-expected-branch-version"));
|
||||
});
|
||||
project.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME, checkTask -> checkTask.dependsOn(checkExpectedBranchVersionTask));
|
||||
}
|
||||
|
||||
private static Spec<Task> skipPropertyFalseOrNotPresent(ProviderFactory providers) {
|
||||
Provider<Boolean> skipPropertyFalseOrNotPresent = providers
|
||||
private static boolean skipPropertyFalseOrNotPresent(Task task) {
|
||||
return task.getProject()
|
||||
.getProviders()
|
||||
.gradleProperty("skipCheckExpectedBranchVersion")
|
||||
.orElse("false")
|
||||
.map("false"::equalsIgnoreCase);
|
||||
return (task) -> skipPropertyFalseOrNotPresent.get();
|
||||
}
|
||||
|
||||
private static boolean isVersionBranch(Task task) {
|
||||
return isVersionBranch((CheckExpectedBranchVersionTask) task);
|
||||
}
|
||||
|
||||
private static boolean isVersionBranch(CheckExpectedBranchVersionTask task) {
|
||||
String branchName = task.getBranchName().getOrNull();
|
||||
if (branchName == null) {
|
||||
return false;
|
||||
}
|
||||
return branchName.matches("^[0-9]+\\.[0-9]+\\.x$");
|
||||
}
|
||||
|
||||
private static Provider<String> getBranchName(ProviderFactory providers, Logger logger) {
|
||||
ExecOutput execOutput = providers.exec((execSpec) -> {
|
||||
execSpec.setCommandLine("git", "symbolic-ref", "--short", "HEAD");
|
||||
execSpec.setIgnoreExitValue(true);
|
||||
});
|
||||
|
||||
return providers.provider(() -> {
|
||||
int exitValue = execOutput.getResult().get().getExitValue();
|
||||
if (exitValue != 0) {
|
||||
logger.warn("Unable to determine branch name. Received exit code '{}' from `git`.", exitValue);
|
||||
logger.warn(execOutput.getStandardError().getAsText().getOrNull());
|
||||
return null;
|
||||
}
|
||||
|
||||
String branchName = execOutput.getStandardOutput().getAsText().get().trim();
|
||||
logger.info("Git branch name is '{}'.", branchName);
|
||||
return branchName;
|
||||
});
|
||||
.map("false"::equalsIgnoreCase)
|
||||
.get();
|
||||
}
|
||||
|
||||
@CacheableTask
|
||||
@ -114,10 +77,15 @@ public class CheckExpectedBranchVersionPlugin implements Plugin<Project> {
|
||||
public void run() {
|
||||
String version = getVersion().get();
|
||||
String branchVersion = getBranchName().map(String::trim).get();
|
||||
if (!branchVersion.matches("^[0-9]+\\.[0-9]+\\.x$")) {
|
||||
String msg = String.format("Branch version [%s] does not match *.x, ignoring", branchVersion);
|
||||
getLogger().warn(msg);
|
||||
writeExpectedVersionOutput(msg);
|
||||
return;
|
||||
}
|
||||
if (!versionsMatch(version, branchVersion)) {
|
||||
String msg = String.format("Project version [%s] does not match branch version [%s]. " +
|
||||
"Please verify that the branch contains the right version. " +
|
||||
"To bypass this check, run the build with -PskipCheckExpectedBranchVersion.", version, branchVersion);
|
||||
"Please verify that the branch contains the right version.", version, branchVersion);
|
||||
writeExpectedVersionOutput(msg);
|
||||
throw new VerificationException(msg);
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken implemen
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable final Object obj) {
|
||||
public boolean equals(final Object obj) {
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -16,16 +16,15 @@
|
||||
|
||||
package org.springframework.security.config.annotation.authorization;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.authorization.AuthorizationManagerFactories;
|
||||
import org.springframework.security.authorization.AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder;
|
||||
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
|
||||
import org.springframework.security.config.Customizer;
|
||||
|
||||
/**
|
||||
* Uses {@link EnableMultiFactorAuthentication} to configure a
|
||||
@ -40,13 +39,10 @@ class AuthorizationManagerFactoryConfiguration implements ImportAware {
|
||||
private String[] authorities;
|
||||
|
||||
@Bean
|
||||
DefaultAuthorizationManagerFactory authorizationManagerFactory(
|
||||
List<Customizer<AdditionalRequiredFactorsBuilder<Object>>> additionalRequiredFactorsCustomizers) {
|
||||
AdditionalRequiredFactorsBuilder<Object> builder = AuthorizationManagerFactories.multiFactor()
|
||||
DefaultAuthorizationManagerFactory authorizationManagerFactory(ObjectProvider<RoleHierarchy> roleHierarchy) {
|
||||
AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object> builder = AuthorizationManagerFactories
|
||||
.multiFactor()
|
||||
.requireFactors(this.authorities);
|
||||
for (Customizer<AdditionalRequiredFactorsBuilder<Object>> customizer : additionalRequiredFactorsCustomizers) {
|
||||
customizer.customize(builder);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
@ -30,12 +30,8 @@ import org.springframework.security.authorization.DefaultAuthorizationManagerFac
|
||||
*
|
||||
* When {@link #authorities()} is specified creates a
|
||||
* {@link DefaultAuthorizationManagerFactory} as a Bean with the {@link #authorities()}
|
||||
* specified as additional required authorities. When {@link #when()} is
|
||||
* {@link MultiFactorCondition#WEBAUTHN_REGISTERED}, {@link #authorities()} must include
|
||||
* {@link org.springframework.security.core.authority.FactorGrantedAuthority#WEBAUTHN_AUTHORITY};
|
||||
* otherwise an {@link IllegalArgumentException} is thrown during configuration
|
||||
* processing. When {@link #when()} is not specified (default is an empty array), no such
|
||||
* requirement applies. The configuration will be picked up by both
|
||||
* specified as additional required authorities. The configuration will be picked up by
|
||||
* both
|
||||
* {@link org.springframework.security.config.annotation.web.configuration.EnableWebSecurity}
|
||||
* and
|
||||
* {@link org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity}.
|
||||
@ -49,19 +45,6 @@ import org.springframework.security.authorization.DefaultAuthorizationManagerFac
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* You can also publish one or more
|
||||
* {@code Customizer<AdditionalRequiredFactorsBuilder<Object>>} beans to further customize
|
||||
* the {@link DefaultAuthorizationManagerFactory}. For example, conditionally applying MFA
|
||||
* for specific users:
|
||||
*
|
||||
* <pre>
|
||||
* @Bean
|
||||
* Customizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object>> additionalRequiredFactorsCustomizer() {
|
||||
* return (builder) -> builder.when((auth) -> "admin".equals(auth.getName()));
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* NOTE: At this time reactive applications do not support MFA and thus are not impacted.
|
||||
* This will likely be enhanced in the future.
|
||||
*
|
||||
@ -84,15 +67,4 @@ public @interface EnableMultiFactorAuthentication {
|
||||
*/
|
||||
String[] authorities();
|
||||
|
||||
/**
|
||||
* The conditions under which multi-factor authentication is required.
|
||||
* <p>
|
||||
* When multiple conditions are specified, they are applied as an AND (all conditions
|
||||
* must be met).
|
||||
* @return the conditions (default is an empty array, which requires MFA
|
||||
* unconditionally)
|
||||
* @since 7.1
|
||||
*/
|
||||
MultiFactorCondition[] when() default {};
|
||||
|
||||
}
|
||||
|
||||
@ -17,24 +17,16 @@
|
||||
package org.springframework.security.config.annotation.authorization;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
|
||||
import org.springframework.security.core.authority.FactorGrantedAuthority;
|
||||
|
||||
/**
|
||||
* Uses {@link EnableMultiFactorAuthentication} to configure a
|
||||
* {@link DefaultAuthorizationManagerFactory}.
|
||||
* <p>
|
||||
* When {@link EnableMultiFactorAuthentication#when()} includes
|
||||
* {@link MultiFactorCondition#WEBAUTHN_REGISTERED}, validates that
|
||||
* {@link EnableMultiFactorAuthentication#authorities()} includes
|
||||
* {@link org.springframework.security.core.authority.FactorGrantedAuthority#WEBAUTHN_AUTHORITY}
|
||||
* and throws an {@link IllegalArgumentException} if not.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 7.0
|
||||
@ -47,19 +39,9 @@ class MultiFactorAuthenticationSelector implements ImportSelector {
|
||||
Map<String, Object> multiFactorAuthenticationAttrs = metadata
|
||||
.getAnnotationAttributes(EnableMultiFactorAuthentication.class.getName());
|
||||
String[] authorities = (String[]) multiFactorAuthenticationAttrs.getOrDefault("authorities", new String[0]);
|
||||
MultiFactorCondition[] when = (MultiFactorCondition[]) multiFactorAuthenticationAttrs.getOrDefault("when",
|
||||
new MultiFactorCondition[0]);
|
||||
boolean hasWebAuthn = Arrays.asList(when).contains(MultiFactorCondition.WEBAUTHN_REGISTERED);
|
||||
if (hasWebAuthn && !Arrays.asList(authorities).contains(FactorGrantedAuthority.WEBAUTHN_AUTHORITY)) {
|
||||
throw new IllegalArgumentException("When when() includes " + MultiFactorCondition.WEBAUTHN_REGISTERED
|
||||
+ ", authorities() must include " + FactorGrantedAuthority.WEBAUTHN_AUTHORITY);
|
||||
}
|
||||
List<String> imports = new ArrayList<>(3);
|
||||
List<String> imports = new ArrayList<>(2);
|
||||
if (authorities.length > 0) {
|
||||
imports.add(AuthorizationManagerFactoryConfiguration.class.getName());
|
||||
if (hasWebAuthn) {
|
||||
imports.add(WhenWebAuthnRegisteredMfaConfiguration.class.getName());
|
||||
}
|
||||
}
|
||||
imports.add(EnableMfaFiltersConfiguration.class.getName());
|
||||
return imports.toArray(new String[imports.size()]);
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.config.annotation.authorization;
|
||||
|
||||
/**
|
||||
* Condition under which multi-factor authentication is required.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 7.1
|
||||
* @see EnableMultiFactorAuthentication#when()
|
||||
*/
|
||||
public enum MultiFactorCondition {
|
||||
|
||||
/**
|
||||
* Require multi-factor authentication only when the user has a WebAuthn credential
|
||||
* record registered.
|
||||
* <p>
|
||||
* When this condition is specified,
|
||||
* {@link EnableMultiFactorAuthentication#authorities()} must include
|
||||
* {@link org.springframework.security.core.authority.FactorGrantedAuthority#WEBAUTHN_AUTHORITY}.
|
||||
* Failing to include it results in an {@link IllegalArgumentException} when the
|
||||
* configuration is processed.
|
||||
* <p>
|
||||
* Using this condition also requires both a
|
||||
* {@link org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository}
|
||||
* Bean and a
|
||||
* {@link org.springframework.security.web.webauthn.management.UserCredentialRepository}
|
||||
* Bean to be published.
|
||||
*
|
||||
* <pre>
|
||||
* @Bean
|
||||
* public PublicKeyCredentialUserEntityRepository userEntityRepository() {
|
||||
* return new InMemoryPublicKeyCredentialUserEntityRepository();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public UserCredentialRepository userCredentialRepository() {
|
||||
* return new InMemoryUserCredentialRepository();
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
WEBAUTHN_REGISTERED
|
||||
|
||||
}
|
||||
@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.config.annotation.authorization;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authorization.AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||
import org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;
|
||||
import org.springframework.security.web.webauthn.management.UserCredentialRepository;
|
||||
|
||||
/**
|
||||
* Configuration that provides a
|
||||
* {@link Customizer}<{@link AdditionalRequiredFactorsBuilder}> for
|
||||
* {@link MultiFactorCondition#WEBAUTHN_REGISTERED}, requiring multi-factor authentication
|
||||
* only when the user has a WebAuthn credential record.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 7.1
|
||||
* @see EnableMultiFactorAuthentication#when()
|
||||
* @see MultiFactorCondition#WEBAUTHN_REGISTERED
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
class WhenWebAuthnRegisteredMfaConfiguration {
|
||||
|
||||
@Bean
|
||||
Customizer<AdditionalRequiredFactorsBuilder<Object>> additionalRequiredFactorsCustomizer(
|
||||
PublicKeyCredentialUserEntityRepository userEntityRepository,
|
||||
UserCredentialRepository userCredentialRepository) {
|
||||
return (builder) -> builder.withWhen((current) -> {
|
||||
Predicate<Authentication> webAuthnRegisteredPredicate = new WebAuthnRegisteredPredicate(
|
||||
userEntityRepository, userCredentialRepository);
|
||||
if (current == null) {
|
||||
return webAuthnRegisteredPredicate;
|
||||
}
|
||||
return current.and(webAuthnRegisteredPredicate);
|
||||
});
|
||||
}
|
||||
|
||||
private static final class WebAuthnRegisteredPredicate implements Predicate<Authentication> {
|
||||
|
||||
private final PublicKeyCredentialUserEntityRepository userEntityRepository;
|
||||
|
||||
private final UserCredentialRepository userCredentialRepository;
|
||||
|
||||
private WebAuthnRegisteredPredicate(PublicKeyCredentialUserEntityRepository userEntityRepository,
|
||||
UserCredentialRepository userCredentialRepository) {
|
||||
this.userEntityRepository = userEntityRepository;
|
||||
this.userCredentialRepository = userCredentialRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Authentication authentication) {
|
||||
if (authentication == null || authentication.getName() == null) {
|
||||
return false;
|
||||
}
|
||||
PublicKeyCredentialUserEntity userEntity = this.userEntityRepository
|
||||
.findByUsername(authentication.getName());
|
||||
if (userEntity == null) {
|
||||
return false;
|
||||
}
|
||||
return !this.userCredentialRepository.findByUserId(userEntity.getId()).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WEBAUTHN_REGISTERED";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -27,7 +27,6 @@ import java.util.function.Supplier;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscription;
|
||||
import reactor.core.CoreSubscriber;
|
||||
@ -287,7 +286,7 @@ class SecurityReactorContextConfiguration {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -21,18 +21,15 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.PreFlightRequestHandler;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import org.springframework.web.filter.PreFlightRequestFilter;
|
||||
|
||||
/**
|
||||
* Adds {@link CorsFilter} or {@link PreFlightRequestFilter} to the Spring Security filter
|
||||
* chain. If a bean by the name of corsFilter is provided, that {@link CorsFilter} is
|
||||
* used. Else if corsConfigurationSource is defined, then that
|
||||
* {@link CorsConfigurationSource} is used. If a {@link PreFlightRequestHandler} is set on
|
||||
* this configurer, {@link CorsFilter} is not used and {@link PreFlightRequestFilter} is
|
||||
* registered instead.
|
||||
* Adds {@link CorsFilter} to the Spring Security filter chain. If a bean by the name of
|
||||
* corsFilter is provided, that {@link CorsFilter} is used. Else if
|
||||
* corsConfigurationSource is defined, then that {@link CorsConfiguration} is used.
|
||||
*
|
||||
* @param <H> the builder to return.
|
||||
* @author Rob Winch
|
||||
@ -46,8 +43,6 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHt
|
||||
|
||||
private CorsConfigurationSource configurationSource;
|
||||
|
||||
private PreFlightRequestHandler preFlightRequestHandler;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
@ -61,85 +56,30 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHt
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given {@link PreFlightRequestHandler} for CORS preflight requests. When
|
||||
* set, {@link CorsFilter} is not used. Cannot be combined with
|
||||
* {@link #configurationSource(CorsConfigurationSource)}.
|
||||
* @param preFlightRequestHandler the handler to use
|
||||
* @return the {@link CorsConfigurer} for additional configuration
|
||||
*/
|
||||
public CorsConfigurer<H> preFlightRequestHandler(PreFlightRequestHandler preFlightRequestHandler) {
|
||||
this.preFlightRequestHandler = preFlightRequestHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(H http) {
|
||||
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
|
||||
|
||||
if (this.configurationSource != null && this.preFlightRequestHandler != null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot configure both a CorsConfigurationSource and a PreFlightRequestHandler on CorsConfigurer");
|
||||
}
|
||||
|
||||
CorsFilter corsFilter = getCorsFilter(context);
|
||||
if (corsFilter != null) {
|
||||
http.addFilter(corsFilter);
|
||||
return;
|
||||
}
|
||||
PreFlightRequestHandler preFlightRequestHandlerBean = getPreFlightRequestHandler(context);
|
||||
if (preFlightRequestHandlerBean != null) {
|
||||
http.addFilterBefore(new PreFlightRequestFilter(preFlightRequestHandlerBean), CorsFilter.class);
|
||||
return;
|
||||
}
|
||||
throw new NoSuchBeanDefinitionException(CorsConfigurationSource.class,
|
||||
"Failed to find a bean that implements `CorsConfigurationSource`. Please ensure that you are using "
|
||||
+ "`@EnableWebMvc`, are publishing a `WebMvcConfigurer`, or are publishing a `CorsConfigurationSource` bean.");
|
||||
}
|
||||
|
||||
private PreFlightRequestHandler getPreFlightRequestHandler(ApplicationContext context) {
|
||||
if (this.configurationSource != null) {
|
||||
return null;
|
||||
}
|
||||
if (this.preFlightRequestHandler != null) {
|
||||
return this.preFlightRequestHandler;
|
||||
}
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
if (context.getBeanNamesForType(PreFlightRequestHandler.class).length > 0) {
|
||||
return context.getBean(PreFlightRequestHandler.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private CorsConfigurationSource getCorsConfigurationSource(ApplicationContext context) {
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
boolean containsCorsSource = context.containsBeanDefinition(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
|
||||
if (containsCorsSource) {
|
||||
return context.getBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME, CorsConfigurationSource.class);
|
||||
}
|
||||
return MvcCorsFilter.getMvcCorsConfigurationSource(context);
|
||||
Assert.state(corsFilter != null, () -> "Please configure either a " + CORS_FILTER_BEAN_NAME + " bean or a "
|
||||
+ CORS_CONFIGURATION_SOURCE_BEAN_NAME + "bean.");
|
||||
http.addFilter(corsFilter);
|
||||
}
|
||||
|
||||
private CorsFilter getCorsFilter(ApplicationContext context) {
|
||||
if (this.preFlightRequestHandler != null) {
|
||||
return null;
|
||||
}
|
||||
if (this.configurationSource != null) {
|
||||
return new CorsFilter(this.configurationSource);
|
||||
}
|
||||
boolean containsCorsFilter = context != null && context.containsBeanDefinition(CORS_FILTER_BEAN_NAME);
|
||||
boolean containsCorsFilter = context.containsBeanDefinition(CORS_FILTER_BEAN_NAME);
|
||||
if (containsCorsFilter) {
|
||||
return context.getBean(CORS_FILTER_BEAN_NAME, CorsFilter.class);
|
||||
}
|
||||
CorsConfigurationSource corsConfigurationSource = getCorsConfigurationSource(context);
|
||||
if (corsConfigurationSource != null) {
|
||||
return new CorsFilter(corsConfigurationSource);
|
||||
boolean containsCorsSource = context.containsBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
|
||||
if (containsCorsSource) {
|
||||
CorsConfigurationSource configurationSource = context.getBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME,
|
||||
CorsConfigurationSource.class);
|
||||
return new CorsFilter(configurationSource);
|
||||
}
|
||||
return null;
|
||||
return MvcCorsFilter.getMvcCorsFilter(context);
|
||||
}
|
||||
|
||||
static class MvcCorsFilter {
|
||||
@ -152,11 +92,15 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHt
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
private static CorsConfigurationSource getMvcCorsConfigurationSource(ApplicationContext context) {
|
||||
private static CorsFilter getMvcCorsFilter(ApplicationContext context) {
|
||||
if (context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
|
||||
return context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, CorsConfigurationSource.class);
|
||||
CorsConfigurationSource corsConfigurationSource = context
|
||||
.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, CorsConfigurationSource.class);
|
||||
return new CorsFilter(corsConfigurationSource);
|
||||
}
|
||||
return null;
|
||||
throw new NoSuchBeanDefinitionException(CorsConfigurationSource.class,
|
||||
"Failed to find a bean that implements `CorsConfigurationSource`. Please ensure that you are using "
|
||||
+ "`@EnableWebMvc`, are publishing a `WebMvcConfigurer`, or are publishing a `CorsConfigurationSource` bean.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -39,7 +39,6 @@ import org.springframework.security.web.webauthn.api.PublicKeyCredentialRpEntity
|
||||
import org.springframework.security.web.webauthn.authentication.PublicKeyCredentialRequestOptionsFilter;
|
||||
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationFilter;
|
||||
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationProvider;
|
||||
import org.springframework.security.web.webauthn.management.CredentialRecordOwnerAuthorizationManager;
|
||||
import org.springframework.security.web.webauthn.management.MapPublicKeyCredentialUserEntityRepository;
|
||||
import org.springframework.security.web.webauthn.management.MapUserCredentialRepository;
|
||||
import org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;
|
||||
@ -181,8 +180,6 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
webAuthnAuthnFilter = postProcess(webAuthnAuthnFilter);
|
||||
WebAuthnRegistrationFilter webAuthnRegistrationFilter = new WebAuthnRegistrationFilter(userCredentials,
|
||||
rpOperations);
|
||||
webAuthnRegistrationFilter.setDeleteCredentialAuthorizationManager(
|
||||
new CredentialRecordOwnerAuthorizationManager(userCredentials, userEntities));
|
||||
PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(
|
||||
rpOperations);
|
||||
if (creationOptionsRepository != null) {
|
||||
|
||||
@ -19,7 +19,6 @@ package org.springframework.security.config.annotation.web
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer
|
||||
import org.springframework.web.cors.CorsConfigurationSource
|
||||
import org.springframework.web.cors.PreFlightRequestHandler
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] CORS using idiomatic Kotlin code.
|
||||
@ -27,14 +26,11 @@ import org.springframework.web.cors.PreFlightRequestHandler
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.3
|
||||
* @property configurationSource the [CorsConfigurationSource] to use.
|
||||
* @property preFlightRequestHandler the [PreFlightRequestHandler] to use instead of [CorsFilter].
|
||||
*/
|
||||
@SecurityMarker
|
||||
class CorsDsl {
|
||||
var configurationSource: CorsConfigurationSource? = null
|
||||
|
||||
var preFlightRequestHandler: PreFlightRequestHandler? = null
|
||||
|
||||
private var disabled = false
|
||||
|
||||
/**
|
||||
@ -46,8 +42,7 @@ class CorsDsl {
|
||||
|
||||
internal fun get(): (CorsConfigurer<HttpSecurity>) -> Unit {
|
||||
return { cors ->
|
||||
configurationSource?.also { cors.configurationSource(it) }
|
||||
preFlightRequestHandler?.also { cors.preFlightRequestHandler(it) }
|
||||
configurationSource?.also { cors.configurationSource(configurationSource) }
|
||||
if (disabled) {
|
||||
cors.disable()
|
||||
}
|
||||
|
||||
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.config.annotation.authorization;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authorization.AuthorizationManagerFactories;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.authority.FactorGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for {@link EnableMultiFactorAuthentication} with
|
||||
* {@link Customizer}<{@link AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder}>.
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration(classes = EnableMultiFactorAuthenticationCustomizerTests.ConfigWithCustomizer.class)
|
||||
class EnableMultiFactorAuthenticationCustomizerTests {
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "user", authorities = "ROLE_USER")
|
||||
void whenCustomizerAppliedThenConditionalMfaUsed() throws Exception {
|
||||
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "admin", authorities = "ROLE_USER")
|
||||
void whenCustomizerAppliedAndConditionTrueThenMfaRequired() throws Exception {
|
||||
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "admin", authorities = { "ROLE_USER", FactorGrantedAuthority.PASSWORD_AUTHORITY,
|
||||
FactorGrantedAuthority.OTT_AUTHORITY })
|
||||
void whenCustomizerAppliedAndConditionTrueWithMfaThenAuthorized() throws Exception {
|
||||
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
@EnableMultiFactorAuthentication(
|
||||
authorities = { FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY })
|
||||
static class ConfigWithCustomizer {
|
||||
|
||||
@Bean
|
||||
Customizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object>> additionalRequiredFactorsCustomizer() {
|
||||
return (builder) -> builder.when((auth) -> "admin".equals(auth.getName()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
MockMvc mvc(WebApplicationContext context) {
|
||||
return MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("deprecation")
|
||||
UserDetailsService userDetailsService() {
|
||||
UserDetails user = User.withDefaultPasswordEncoder()
|
||||
.username("user")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
.build();
|
||||
UserDetails admin = User.withDefaultPasswordEncoder()
|
||||
.username("admin")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
.build();
|
||||
return new InMemoryUserDetailsManager(user, admin);
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class OkController {
|
||||
|
||||
@GetMapping("/")
|
||||
String ok() {
|
||||
return "ok";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.config.annotation.authorization;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.authorization.AuthorizationManagerFactories;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.authority.FactorGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for {@link EnableMultiFactorAuthentication} with multiple
|
||||
* {@link Customizer}<{@link AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder}>
|
||||
* beans.
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration(
|
||||
classes = EnableMultiFactorAuthenticationMultipleCustomizersTests.ConfigWithMultipleCustomizers.class)
|
||||
class EnableMultiFactorAuthenticationMultipleCustomizersTests {
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "user", authorities = "ROLE_USER")
|
||||
void whenCustomizerAppliedThenConditionalMfaUsed() throws Exception {
|
||||
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "admin", authorities = "ROLE_USER")
|
||||
void whenCustomizersAppliedAndConditionTrueThenMfaRequired() throws Exception {
|
||||
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "admin", authorities = { "ROLE_USER", FactorGrantedAuthority.PASSWORD_AUTHORITY,
|
||||
FactorGrantedAuthority.OTT_AUTHORITY })
|
||||
void whenCustomizersAppliedAndConditionTrueWithMfaThenAuthorized() throws Exception {
|
||||
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "manager", authorities = "ROLE_USER")
|
||||
void whenSecondCustomizerAppliedAndConditionTrueThenMfaRequired() throws Exception {
|
||||
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
@EnableMultiFactorAuthentication(
|
||||
authorities = { FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY })
|
||||
static class ConfigWithMultipleCustomizers {
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
Customizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object>> adminCustomizer() {
|
||||
return (builder) -> builder.withWhen(
|
||||
(current) -> (auth) -> "admin".equals(auth.getName()) || (current != null && current.test(auth)));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(2)
|
||||
Customizer<AuthorizationManagerFactories.AdditionalRequiredFactorsBuilder<Object>> managerCustomizer() {
|
||||
return (builder) -> builder.withWhen(
|
||||
(current) -> (auth) -> "manager".equals(auth.getName()) || (current != null && current.test(auth)));
|
||||
}
|
||||
|
||||
@Bean
|
||||
MockMvc mvc(WebApplicationContext context) {
|
||||
return MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("deprecation")
|
||||
UserDetailsService userDetailsService() {
|
||||
UserDetails user = User.withDefaultPasswordEncoder()
|
||||
.username("user")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
.build();
|
||||
UserDetails admin = User.withDefaultPasswordEncoder()
|
||||
.username("admin")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
.build();
|
||||
UserDetails manager = User.withDefaultPasswordEncoder()
|
||||
.username("manager")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
.build();
|
||||
return new InMemoryUserDetailsManager(user, admin, manager);
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class OkController {
|
||||
|
||||
@GetMapping("/")
|
||||
String ok() {
|
||||
return "ok";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.config.annotation.authorization;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.core.authority.FactorGrantedAuthority;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link MultiFactorAuthenticationSelector}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class MultiFactorAuthenticationSelectorTests {
|
||||
|
||||
private final MultiFactorAuthenticationSelector selector = new MultiFactorAuthenticationSelector();
|
||||
|
||||
@Test
|
||||
void selectImportsWhenWhenIsEmptyAndAuthoritiesSpecifiedThenReturnsImportsWithoutWebAuthnConfig() {
|
||||
AnnotationMetadata metadata = metadata(new MultiFactorCondition[0], FactorGrantedAuthority.OTT_AUTHORITY,
|
||||
FactorGrantedAuthority.PASSWORD_AUTHORITY);
|
||||
String[] imports = this.selector.selectImports(metadata);
|
||||
assertThat(imports).isNotEmpty();
|
||||
assertThat(imports).doesNotContain(WhenWebAuthnRegisteredMfaConfiguration.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectImportsWhenWhenOmittedThenDefaultsToEmptyAndReturnsImports() {
|
||||
AnnotationMetadata metadata = metadataWithoutWhen(FactorGrantedAuthority.OTT_AUTHORITY,
|
||||
FactorGrantedAuthority.PASSWORD_AUTHORITY);
|
||||
String[] imports = this.selector.selectImports(metadata);
|
||||
assertThat(imports).isNotEmpty();
|
||||
assertThat(imports).doesNotContain(WhenWebAuthnRegisteredMfaConfiguration.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectImportsWhenHasWebAuthnConditionAndAuthoritiesIncludesWebAuthnThenReturnsImports() {
|
||||
AnnotationMetadata metadata = metadata(new MultiFactorCondition[] { MultiFactorCondition.WEBAUTHN_REGISTERED },
|
||||
FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY,
|
||||
FactorGrantedAuthority.WEBAUTHN_AUTHORITY);
|
||||
String[] imports = this.selector.selectImports(metadata);
|
||||
assertThat(imports).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectImportsWhenHasWebAuthnConditionAndAuthoritiesOnlyWebAuthnThenReturnsImports() {
|
||||
AnnotationMetadata metadata = metadata(new MultiFactorCondition[] { MultiFactorCondition.WEBAUTHN_REGISTERED },
|
||||
FactorGrantedAuthority.WEBAUTHN_AUTHORITY);
|
||||
String[] imports = this.selector.selectImports(metadata);
|
||||
assertThat(imports).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectImportsWhenHasWebAuthnConditionAndAuthoritiesEmptyThenThrowsException() {
|
||||
AnnotationMetadata metadata = metadata(new MultiFactorCondition[] { MultiFactorCondition.WEBAUTHN_REGISTERED });
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.selector.selectImports(metadata))
|
||||
.withMessageContaining("authorities() must include " + FactorGrantedAuthority.WEBAUTHN_AUTHORITY);
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectImportsWhenHasWebAuthnConditionAndAuthoritiesExcludesWebAuthnThenThrowsException() {
|
||||
AnnotationMetadata metadata = metadata(new MultiFactorCondition[] { MultiFactorCondition.WEBAUTHN_REGISTERED },
|
||||
FactorGrantedAuthority.OTT_AUTHORITY, FactorGrantedAuthority.PASSWORD_AUTHORITY);
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.selector.selectImports(metadata))
|
||||
.withMessageContaining("authorities() must include " + FactorGrantedAuthority.WEBAUTHN_AUTHORITY);
|
||||
}
|
||||
|
||||
private static AnnotationMetadata metadata(MultiFactorCondition[] when, String... authorities) {
|
||||
AnnotationMetadata metadata = mock(AnnotationMetadata.class);
|
||||
Map<String, Object> attrs = new HashMap<>();
|
||||
attrs.put("authorities", authorities);
|
||||
attrs.put("when", when);
|
||||
given(metadata.getAnnotationAttributes(EnableMultiFactorAuthentication.class.getName())).willReturn(attrs);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private static AnnotationMetadata metadataWithoutWhen(String... authorities) {
|
||||
AnnotationMetadata metadata = mock(AnnotationMetadata.class);
|
||||
Map<String, Object> attrs = new HashMap<>();
|
||||
attrs.put("authorities", authorities);
|
||||
given(metadata.getAnnotationAttributes(EnableMultiFactorAuthentication.class.getName())).willReturn(attrs);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
}
|
||||
@ -40,7 +40,6 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.PreFlightRequestHandler;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
@ -197,73 +196,6 @@ public class CorsConfigurerTests {
|
||||
.andExpect(header().exists("X-Content-Type-Options"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void optionsWhenPreFlightRequestHandlerBeanThenHandled() throws Exception {
|
||||
this.spring.register(PreFlightRequestHandlerConfig.class).autowire();
|
||||
this.mvc
|
||||
.perform(options("/")
|
||||
.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
|
||||
.header(HttpHeaders.ORIGIN, "https://example.com"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().exists("X-Pre-Flight"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void optionsWhenNoPreFlightRequestHandlerBeanThenCorsFilterUsed() throws Exception {
|
||||
this.spring.register(NoPreFlightRequestHandlerConfig.class).autowire();
|
||||
this.mvc
|
||||
.perform(options("/")
|
||||
.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
|
||||
.header(HttpHeaders.ORIGIN, "https://example.com"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().exists("Access-Control-Allow-Origin"))
|
||||
.andExpect(header().doesNotExist("X-Pre-Flight"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void optionsWhenExplicitConfigurationSourceThenPreFlightRequestHandlerBeanIgnored() throws Exception {
|
||||
this.spring.register(ExplicitConfigurationSourceWithPreFlightRequestHandlerBeanConfig.class).autowire();
|
||||
this.mvc
|
||||
.perform(options("/")
|
||||
.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
|
||||
.header(HttpHeaders.ORIGIN, "https://example.com"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().exists("Access-Control-Allow-Origin"))
|
||||
.andExpect(header().doesNotExist("X-Pre-Flight"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void optionsWhenPreFlightRequestHandlerMemberThenHandled() throws Exception {
|
||||
this.spring.register(PreFlightRequestHandlerMemberConfig.class).autowire();
|
||||
this.mvc
|
||||
.perform(options("/")
|
||||
.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
|
||||
.header(HttpHeaders.ORIGIN, "https://example.com"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().exists("X-Pre-Flight"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenBothConfigurationSourceAndPreFlightRequestHandlerMemberThenIllegalState() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.spring.register(BothCorsConfigurerMembersConfig.class).autowire())
|
||||
.havingRootCause()
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.withMessageContaining("Cannot configure both");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void optionsWhenPreFlightRequestHandlerMemberThenCorsFilterBeanIgnored() throws Exception {
|
||||
this.spring.register(PreFlightRequestHandlerMemberWithCorsFilterBeanConfig.class).autowire();
|
||||
this.mvc
|
||||
.perform(options("/")
|
||||
.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
|
||||
.header(HttpHeaders.ORIGIN, "https://example.com"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().exists("X-Pre-Flight"))
|
||||
.andExpect(header().doesNotExist("Access-Control-Allow-Origin"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class DefaultCorsConfig {
|
||||
@ -450,150 +382,4 @@ public class CorsConfigurerTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class PreFlightRequestHandlerConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().authenticated())
|
||||
.cors(withDefaults());
|
||||
return http.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
PreFlightRequestHandler preFlightRequestHandler() {
|
||||
return (request, response) -> response.addHeader("X-Pre-Flight", "Handled");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class NoPreFlightRequestHandlerConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().authenticated())
|
||||
.cors(withDefaults());
|
||||
return http.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
CorsConfigurationSource corsConfigurationSource() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));
|
||||
corsConfiguration.setAllowedMethods(Arrays.asList(RequestMethod.GET.name(), RequestMethod.POST.name()));
|
||||
source.registerCorsConfiguration("/**", corsConfiguration);
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class ExplicitConfigurationSourceWithPreFlightRequestHandlerBeanConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));
|
||||
corsConfiguration.setAllowedMethods(Arrays.asList(RequestMethod.GET.name(), RequestMethod.POST.name()));
|
||||
source.registerCorsConfiguration("/**", corsConfiguration);
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().authenticated())
|
||||
.cors((cors) -> cors.configurationSource(source));
|
||||
return http.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
PreFlightRequestHandler preFlightRequestHandler() {
|
||||
return (request, response) -> response.addHeader("X-Pre-Flight", "Handled");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class PreFlightRequestHandlerMemberConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().authenticated())
|
||||
.cors((cors) -> cors.preFlightRequestHandler(
|
||||
(request, response) -> response.addHeader("X-Pre-Flight", "Member")));
|
||||
return http.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class BothCorsConfigurerMembersConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));
|
||||
corsConfiguration.setAllowedMethods(Arrays.asList(RequestMethod.GET.name(), RequestMethod.POST.name()));
|
||||
source.registerCorsConfiguration("/**", corsConfiguration);
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().authenticated())
|
||||
.cors((cors) -> cors
|
||||
.configurationSource(source)
|
||||
.preFlightRequestHandler((request, response) -> response.addHeader("X-Pre-Flight", "Handled")));
|
||||
return http.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class PreFlightRequestHandlerMemberWithCorsFilterBeanConfig {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeHttpRequests((requests) -> requests
|
||||
.anyRequest().authenticated())
|
||||
.cors((cors) -> cors.preFlightRequestHandler(
|
||||
(request, response) -> response.addHeader("X-Pre-Flight", "Member")));
|
||||
return http.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
CorsFilter corsFilter() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));
|
||||
corsConfiguration.setAllowedMethods(Arrays.asList(RequestMethod.GET.name(), RequestMethod.POST.name()));
|
||||
source.registerCorsConfiguration("/**", corsConfiguration);
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -43,16 +43,9 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
import org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
import org.springframework.security.web.webauthn.api.TestCredentialRecords;
|
||||
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;
|
||||
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationFilter;
|
||||
import org.springframework.security.web.webauthn.management.MapPublicKeyCredentialUserEntityRepository;
|
||||
import org.springframework.security.web.webauthn.management.MapUserCredentialRepository;
|
||||
import org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;
|
||||
import org.springframework.security.web.webauthn.management.UserCredentialRepository;
|
||||
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
|
||||
import org.springframework.security.web.webauthn.registration.HttpSessionPublicKeyCredentialCreationOptionsRepository;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
@ -65,7 +58,6 @@ import static org.mockito.BDDMockito.willAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
@ -265,24 +257,6 @@ public class WebAuthnConfigurerTests {
|
||||
.andExpect(content().string(expectedBody));
|
||||
}
|
||||
|
||||
@Test
|
||||
void webauthnWhenDeleteAndCredentialBelongsToUserThenNoContent() throws Exception {
|
||||
this.spring.register(DeleteCredentialConfiguration.class).autowire();
|
||||
this.mvc
|
||||
.perform(delete("/webauthn/register/" + DeleteCredentialConfiguration.CREDENTIAL_ID_BASE64URL)
|
||||
.with(authentication(new TestingAuthenticationToken("user", "password", "ROLE_USER"))))
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void webauthnWhenDeleteAndCredentialBelongsToDifferentUserThenForbidden() throws Exception {
|
||||
this.spring.register(DeleteCredentialConfiguration.class).autowire();
|
||||
this.mvc
|
||||
.perform(delete("/webauthn/register/" + DeleteCredentialConfiguration.CREDENTIAL_ID_BASE64URL)
|
||||
.with(authentication(new TestingAuthenticationToken("other-user", "password", "ROLE_USER"))))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class ConfigCredentialCreationOptionsRepository {
|
||||
@ -501,47 +475,4 @@ public class WebAuthnConfigurerTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class DeleteCredentialConfiguration {
|
||||
|
||||
static final String CREDENTIAL_ID_BASE64URL = "NauGCN7bZ5jEBwThcde51g";
|
||||
|
||||
static final Bytes USER_ENTITY_ID = Bytes.fromBase64("vKBFhsWT3gQnn-gHdT4VXIvjDkVXVYg5w8CLGHPunMM");
|
||||
|
||||
@Bean
|
||||
UserDetailsService userDetailsService() {
|
||||
return new InMemoryUserDetailsManager();
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebAuthnRelyingPartyOperations webAuthnRelyingPartyOperations() {
|
||||
return mock(WebAuthnRelyingPartyOperations.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
UserCredentialRepository userCredentialRepository() {
|
||||
MapUserCredentialRepository repository = new MapUserCredentialRepository();
|
||||
repository.save(TestCredentialRecords.userCredential().build());
|
||||
return repository;
|
||||
}
|
||||
|
||||
@Bean
|
||||
PublicKeyCredentialUserEntityRepository userEntityRepository() {
|
||||
MapPublicKeyCredentialUserEntityRepository repository = new MapPublicKeyCredentialUserEntityRepository();
|
||||
repository.save(ImmutablePublicKeyCredentialUserEntity.builder()
|
||||
.name("user")
|
||||
.id(USER_ENTITY_ID)
|
||||
.displayName("User")
|
||||
.build());
|
||||
return repository;
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
return http.csrf(AbstractHttpConfigurer::disable).webAuthn(Customizer.withDefaults()).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ public class ClientRegistrationsBeanDefinitionParserTests {
|
||||
.isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
|
||||
assertThat(googleRegistration.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
|
||||
assertThat(googleRegistration.getRedirectUri()).isEqualTo("{baseUrl}/{action}/oauth2/code/{registrationId}");
|
||||
assertThat(googleRegistration.getScopes()).isEmpty();
|
||||
assertThat(googleRegistration.getScopes()).isNull();
|
||||
assertThat(googleRegistration.getClientName()).isEqualTo(serverUrl);
|
||||
ProviderDetails googleProviderDetails = googleRegistration.getProviderDetails();
|
||||
assertThat(googleProviderDetails).isNotNull();
|
||||
|
||||
@ -16,10 +16,7 @@
|
||||
|
||||
package org.springframework.security.config.annotation.web
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.assertj.core.api.Assertions.catchThrowable
|
||||
import org.assertj.core.util.Throwables
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.springframework.beans.factory.BeanCreationException
|
||||
@ -38,14 +35,9 @@ import org.springframework.test.web.servlet.get
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RequestMethod
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.header
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||
import org.springframework.web.cors.CorsConfiguration
|
||||
import org.springframework.web.cors.CorsConfigurationSource
|
||||
import org.springframework.web.cors.PreFlightRequestHandler
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
|
||||
import org.springframework.web.filter.CorsFilter
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc
|
||||
|
||||
/**
|
||||
@ -161,7 +153,7 @@ class CorsDslTests {
|
||||
|
||||
@Test
|
||||
fun `CORS when CORS configuration source dsl then responds with CORS header`() {
|
||||
this.spring.register(CorsCrossOriginSourceConfig::class.java, HomeController::class.java).autowire()
|
||||
this.spring.register(CorsCrossOriginBeanConfig::class.java, HomeController::class.java).autowire()
|
||||
|
||||
this.mockMvc.get("/")
|
||||
{
|
||||
@ -193,117 +185,6 @@ class CorsDslTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `CORS when preFlight request handler dsl then OPTIONS uses handler`() {
|
||||
this.spring.register(PreFlightRequestHandlerDslConfig::class.java).autowire()
|
||||
|
||||
this.mockMvc.perform(options("/")
|
||||
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, RequestMethod.POST.name)
|
||||
.header(HttpHeaders.ORIGIN, "https://example.com"))
|
||||
.andExpect(status().isOk)
|
||||
.andExpect(header().exists("X-Pre-Flight"))
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class PreFlightRequestHandlerDslConfig {
|
||||
@Bean
|
||||
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
authorizeHttpRequests {
|
||||
authorize(anyRequest, authenticated)
|
||||
}
|
||||
cors {
|
||||
preFlightRequestHandler = PreFlightRequestHandler { _, response ->
|
||||
response.addHeader("X-Pre-Flight", "Dsl")
|
||||
}
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `CORS when configuration source and preFlight handler dsl then illegal state`() {
|
||||
val thrown = catchThrowable {
|
||||
this.spring.register(BothCorsDslMembersConfig::class.java).autowire()
|
||||
}
|
||||
assertThat(thrown).isInstanceOf(BeanCreationException::class.java)
|
||||
assertThat(Throwables.getRootCause(thrown))
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessageContaining("Cannot configure both")
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class BothCorsDslMembersConfig {
|
||||
@Bean
|
||||
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
val source = UrlBasedCorsConfigurationSource()
|
||||
val corsConfiguration = CorsConfiguration()
|
||||
corsConfiguration.allowedOrigins = listOf("*")
|
||||
corsConfiguration.allowedMethods = listOf(
|
||||
RequestMethod.GET.name,
|
||||
RequestMethod.POST.name)
|
||||
source.registerCorsConfiguration("/**", corsConfiguration)
|
||||
http {
|
||||
authorizeHttpRequests {
|
||||
authorize(anyRequest, authenticated)
|
||||
}
|
||||
cors {
|
||||
configurationSource = source
|
||||
preFlightRequestHandler = PreFlightRequestHandler { _, response ->
|
||||
response.addHeader("X-Pre-Flight", "Dsl")
|
||||
}
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `CORS when preFlight handler dsl then CorsFilter bean ignored on OPTIONS`() {
|
||||
this.spring.register(PreFlightRequestHandlerDslWithCorsFilterBeanConfig::class.java).autowire()
|
||||
|
||||
this.mockMvc.perform(options("/")
|
||||
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, RequestMethod.POST.name)
|
||||
.header(HttpHeaders.ORIGIN, "https://example.com"))
|
||||
.andExpect(status().isOk)
|
||||
.andExpect(header().exists("X-Pre-Flight"))
|
||||
.andExpect(header().doesNotExist("Access-Control-Allow-Origin"))
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class PreFlightRequestHandlerDslWithCorsFilterBeanConfig {
|
||||
@Bean
|
||||
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
authorizeHttpRequests {
|
||||
authorize(anyRequest, authenticated)
|
||||
}
|
||||
cors {
|
||||
preFlightRequestHandler = PreFlightRequestHandler { _, response ->
|
||||
response.addHeader("X-Pre-Flight", "Dsl")
|
||||
}
|
||||
}
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun corsFilter(): CorsFilter {
|
||||
val source = UrlBasedCorsConfigurationSource()
|
||||
val corsConfiguration = CorsConfiguration()
|
||||
corsConfiguration.allowedOrigins = listOf("*")
|
||||
corsConfiguration.allowedMethods = listOf(
|
||||
RequestMethod.GET.name,
|
||||
RequestMethod.POST.name)
|
||||
source.registerCorsConfiguration("/**", corsConfiguration)
|
||||
return CorsFilter(source)
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
private class HomeController {
|
||||
@GetMapping("/")
|
||||
|
||||
@ -73,11 +73,13 @@ class AuthorizationEndpointDslTests {
|
||||
|
||||
companion object {
|
||||
val RESOLVER: OAuth2AuthorizationRequestResolver = object : OAuth2AuthorizationRequestResolver {
|
||||
override fun resolve(request: HttpServletRequest) =
|
||||
OAuth2AuthorizationRequest.authorizationCode().build()
|
||||
override fun resolve(
|
||||
request: HttpServletRequest?
|
||||
) = OAuth2AuthorizationRequest.authorizationCode().build()
|
||||
|
||||
override fun resolve(request: HttpServletRequest, clientRegistrationId: String) =
|
||||
OAuth2AuthorizationRequest.authorizationCode().build()
|
||||
override fun resolve(
|
||||
request: HttpServletRequest?, clientRegistrationId: String?
|
||||
) = OAuth2AuthorizationRequest.authorizationCode().build()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user