mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-23 20:42:14 +00:00
Support Custom RequestMatchers for WebAuthn
Closes gh-16517 Signed-off-by: topiam <support@topiam.cn>
This commit is contained in:
parent
fa35c5b4d8
commit
85f0f3f34a
@ -75,6 +75,17 @@ public class PublicKeyCredentialRequestOptionsFilter extends OncePerRequestFilte
|
||||
this.rpOptions = rpOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RequestMatcher} used to trigger this filter. By default, the
|
||||
* {@link RequestMatcher} is {@code POST /webauthn/authenticate/options}.
|
||||
* @param requestMatcher the {@link RequestMatcher} to use
|
||||
* @since 6.5
|
||||
*/
|
||||
public void setRequestMatcher(RequestMatcher requestMatcher) {
|
||||
Assert.notNull(requestMatcher, "requestMatcher cannot be null");
|
||||
this.matcher = requestMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
@ -82,6 +82,18 @@ public class PublicKeyCredentialCreationOptionsFilter extends OncePerRequestFilt
|
||||
this.rpOperations = rpOperations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RequestMatcher} used to trigger this filter.
|
||||
* <p>
|
||||
* By default, the {@link RequestMatcher} is {@code POST /webauthn/register/options}.
|
||||
* @param requestMatcher the {@link RequestMatcher} to use
|
||||
* @since 6.5
|
||||
*/
|
||||
public void setRequestMatcher(RequestMatcher requestMatcher) {
|
||||
Assert.notNull(requestMatcher, "requestMatcher cannot be null");
|
||||
this.matcher = requestMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
@ -105,6 +105,32 @@ public class WebAuthnRegistrationFilter extends OncePerRequestFilter {
|
||||
this.rpOptions = rpOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RequestMatcher} to trigger this filter's the credential
|
||||
* registration operation .
|
||||
* <p/>
|
||||
* By default, the {@link RequestMatcher} is {@code POST /webauthn/register}.
|
||||
* @param registerCredentialMatcher the {@link RequestMatcher} to use
|
||||
* @since 6.5
|
||||
*/
|
||||
public void setRegisterCredentialMatcher(RequestMatcher registerCredentialMatcher) {
|
||||
Assert.notNull(registerCredentialMatcher, "registerCredentialMatcher cannot be null");
|
||||
this.registerCredentialMatcher = registerCredentialMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RequestMatcher} to trigger this filter's the credential removal
|
||||
* operation .
|
||||
* <p/>
|
||||
* By default, the {@link RequestMatcher} is {@code DELETE /webauthn/register/{id}}.
|
||||
* @param removeCredentialMatcher the {@link RequestMatcher} to use
|
||||
* @since 6.5
|
||||
*/
|
||||
public void setRemoveCredentialMatcher(RequestMatcher removeCredentialMatcher) {
|
||||
Assert.notNull(removeCredentialMatcher, "removeCredentialMatcher cannot be null");
|
||||
this.removeCredentialMatcher = removeCredentialMatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
@ -18,6 +18,7 @@ package org.springframework.security.web.webauthn.authentication;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -30,10 +31,13 @@ import org.skyscreamer.jsonassert.JSONAssert;
|
||||
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialRequestOptions;
|
||||
@ -48,6 +52,8 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.verifyNoInteractions;
|
||||
import static org.mockito.BDDMockito.willAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
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.status;
|
||||
@ -75,6 +81,10 @@ class PublicKeyCredentialRequestOptionsFilterTests {
|
||||
|
||||
private PublicKeyCredentialRequestOptionsFilter filter;
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@BeforeEach
|
||||
@ -82,6 +92,8 @@ class PublicKeyCredentialRequestOptionsFilterTests {
|
||||
this.filter = new PublicKeyCredentialRequestOptionsFilter(this.relyingPartyOperations);
|
||||
this.filter.setRequestOptionsRepository(this.requestOptionsRepository);
|
||||
this.mockMvc = MockMvcBuilders.standaloneSetup().addFilter(this.filter).build();
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.response = new MockHttpServletResponse();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@ -89,6 +101,15 @@ class PublicKeyCredentialRequestOptionsFilterTests {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterWhenCustomRequestMatcherThenUses() throws Exception {
|
||||
RequestMatcher requestMatcher = mock(RequestMatcher.class);
|
||||
this.filter.setRequestMatcher(requestMatcher);
|
||||
FilterChain mock = mock(FilterChain.class);
|
||||
this.filter.doFilter(this.request, this.response, mock);
|
||||
verify(requestMatcher).matches(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorWhenNull() {
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
|
@ -18,7 +18,9 @@ package org.springframework.security.web.webauthn.registration;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
@ -27,12 +29,14 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
|
||||
import org.springframework.security.web.webauthn.api.Bytes;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
@ -47,6 +51,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
@ -68,11 +74,38 @@ class PublicKeyCredentialCreationOptionsFilterTests {
|
||||
@Mock
|
||||
private WebAuthnRelyingPartyOperations rpOperations;
|
||||
|
||||
private PublicKeyCredentialCreationOptionsFilter filter;
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.filter = new PublicKeyCredentialCreationOptionsFilter(this.rpOperations);
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.response = new MockHttpServletResponse();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void clear() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterWhenCustomRequestMatcherThenUses() throws Exception {
|
||||
RequestMatcher requestMatcher = mock(RequestMatcher.class);
|
||||
this.filter.setRequestMatcher(requestMatcher);
|
||||
FilterChain mock = mock(FilterChain.class);
|
||||
this.filter.doFilter(this.request, this.response, mock);
|
||||
verify(requestMatcher).matches(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setRequestMatcherWhenNullThenIllegalArgument() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRequestMatcher(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorWhenRpOperationsIsNullThenIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new PublicKeyCredentialCreationOptionsFilter(null))
|
||||
|
@ -30,6 +30,7 @@ import org.springframework.http.converter.GenericHttpMessageConverter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;
|
||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
|
||||
import org.springframework.security.web.webauthn.api.TestCredentialRecord;
|
||||
@ -100,9 +101,42 @@ class WebAuthnRegistrationFilterTests {
|
||||
|
||||
private WebAuthnRegistrationFilter filter;
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.filter = new WebAuthnRegistrationFilter(this.userCredentials, this.operations);
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.response = new MockHttpServletResponse();
|
||||
this.chain = mock(FilterChain.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterWhenCustomRequestRegisterCredentialMatcherThenUses() throws Exception {
|
||||
RequestMatcher requestMatcher = mock(RequestMatcher.class);
|
||||
this.filter.setRegisterCredentialMatcher(requestMatcher);
|
||||
FilterChain mock = mock(FilterChain.class);
|
||||
this.filter.doFilter(this.request, this.response, mock);
|
||||
verify(requestMatcher).matches(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterWhenCustomRequestRemoveCredentialMatcherThenUses() throws Exception {
|
||||
RequestMatcher requestMatcher = mock(RequestMatcher.class);
|
||||
this.filter.setRemoveCredentialMatcher(requestMatcher);
|
||||
FilterChain mock = mock(FilterChain.class);
|
||||
this.filter.doFilter(this.request, this.response, mock);
|
||||
verify(requestMatcher).matches(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setRequestRegisterCredentialWhenNullThenIllegalArgument() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRegisterCredentialMatcher(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void setRequestRemoveCredentialWhenNullThenIllegalArgument() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setRemoveCredentialMatcher(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
x
Reference in New Issue
Block a user