25 changed files with 1307 additions and 4 deletions
@ -0,0 +1,62 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.web.configurers; |
||||||
|
|
||||||
|
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; |
||||||
|
import org.springframework.security.web.RequestMatcherRedirectFilter; |
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds password management support. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
* @since 5.6 |
||||||
|
*/ |
||||||
|
public final class PasswordManagementConfigurer<B extends HttpSecurityBuilder<B>> |
||||||
|
extends AbstractHttpConfigurer<PasswordManagementConfigurer<B>, B> { |
||||||
|
|
||||||
|
private static final String WELL_KNOWN_CHANGE_PASSWORD_PATTERN = "/.well-known/change-password"; |
||||||
|
|
||||||
|
private static final String DEFAULT_CHANGE_PASSWORD_PAGE = "/change-password"; |
||||||
|
|
||||||
|
private String changePasswordPage = DEFAULT_CHANGE_PASSWORD_PAGE; |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the change password page. Defaults to |
||||||
|
* {@link PasswordManagementConfigurer#DEFAULT_CHANGE_PASSWORD_PAGE}. |
||||||
|
* @param changePasswordPage the change password page |
||||||
|
* @return the {@link PasswordManagementConfigurer} for further customizations |
||||||
|
*/ |
||||||
|
public PasswordManagementConfigurer<B> changePasswordPage(String changePasswordPage) { |
||||||
|
Assert.hasText(changePasswordPage, "changePasswordPage cannot be empty"); |
||||||
|
this.changePasswordPage = changePasswordPage; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void configure(B http) throws Exception { |
||||||
|
RequestMatcherRedirectFilter changePasswordFilter = new RequestMatcherRedirectFilter( |
||||||
|
new AntPathRequestMatcher(WELL_KNOWN_CHANGE_PASSWORD_PATTERN), this.changePasswordPage); |
||||||
|
http.addFilterBefore(postProcess(changePasswordFilter), UsernamePasswordAuthenticationFilter.class); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,61 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.http; |
||||||
|
|
||||||
|
import org.w3c.dom.Element; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition; |
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder; |
||||||
|
import org.springframework.beans.factory.xml.BeanDefinitionParser; |
||||||
|
import org.springframework.beans.factory.xml.ParserContext; |
||||||
|
import org.springframework.security.web.RequestMatcherRedirectFilter; |
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; |
||||||
|
import org.springframework.util.StringUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* The bean definition parser for a Well-Known URL for Changing Passwords. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
* @since 5.6 |
||||||
|
*/ |
||||||
|
public final class WellKnownChangePasswordBeanDefinitionParser implements BeanDefinitionParser { |
||||||
|
|
||||||
|
private static final String WELL_KNOWN_CHANGE_PASSWORD_PATTERN = "/.well-known/change-password"; |
||||||
|
|
||||||
|
private static final String DEFAULT_CHANGE_PASSWORD_PAGE = "/change-password"; |
||||||
|
|
||||||
|
private static final String ATT_CHANGE_PASSWORD_PAGE = "change-password-page"; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public BeanDefinition parse(Element element, ParserContext parserContext) { |
||||||
|
BeanDefinition changePasswordFilter = BeanDefinitionBuilder |
||||||
|
.rootBeanDefinition(RequestMatcherRedirectFilter.class) |
||||||
|
.addConstructorArgValue(new AntPathRequestMatcher(WELL_KNOWN_CHANGE_PASSWORD_PATTERN)) |
||||||
|
.addConstructorArgValue(getChangePasswordPage(element)).getBeanDefinition(); |
||||||
|
parserContext.getReaderContext().registerWithGeneratedName(changePasswordFilter); |
||||||
|
return changePasswordFilter; |
||||||
|
} |
||||||
|
|
||||||
|
private String getChangePasswordPage(Element element) { |
||||||
|
String changePasswordPage = element.getAttribute(ATT_CHANGE_PASSWORD_PAGE); |
||||||
|
return (StringUtils.hasText(changePasswordPage) ? changePasswordPage : DEFAULT_CHANGE_PASSWORD_PAGE); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.web.server |
||||||
|
|
||||||
|
/** |
||||||
|
* A Kotlin DSL to configure [ServerHttpSecurity] password management |
||||||
|
* using idiomatic Kotlin code. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
* @property changePasswordPage the change password page. |
||||||
|
* @since 5.6 |
||||||
|
*/ |
||||||
|
@ServerSecurityMarker |
||||||
|
class ServerPasswordManagementDsl { |
||||||
|
var changePasswordPage: String? = null |
||||||
|
|
||||||
|
internal fun get(): (ServerHttpSecurity.PasswordManagementSpec) -> Unit { |
||||||
|
return { passwordManagement -> |
||||||
|
changePasswordPage?.also { passwordManagement.changePasswordPage(changePasswordPage) } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.web.servlet |
||||||
|
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||||
|
import org.springframework.security.config.annotation.web.configurers.PasswordManagementConfigurer |
||||||
|
|
||||||
|
/** |
||||||
|
* A Kotlin DSL to configure [HttpSecurity] password management |
||||||
|
* using idiomatic Kotlin code. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
* @property changePasswordPage the change password page. |
||||||
|
* @since 5.6 |
||||||
|
*/ |
||||||
|
@SecurityMarker |
||||||
|
class PasswordManagementDsl { |
||||||
|
var changePasswordPage: String? = null |
||||||
|
|
||||||
|
internal fun get(): (PasswordManagementConfigurer<HttpSecurity>) -> Unit { |
||||||
|
return { passwordManagement -> |
||||||
|
changePasswordPage?.also { passwordManagement.changePasswordPage(changePasswordPage) } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,116 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.web.configurers; |
||||||
|
|
||||||
|
import org.junit.Rule; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
||||||
|
import org.springframework.security.config.test.SpringTestRule; |
||||||
|
import org.springframework.security.web.SecurityFilterChain; |
||||||
|
import org.springframework.test.web.servlet.MockMvc; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||||
|
import static org.springframework.security.config.Customizer.withDefaults; |
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; |
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; |
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link PasswordManagementConfigurer}. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
*/ |
||||||
|
public class PasswordManagementConfigurerTests { |
||||||
|
|
||||||
|
@Rule |
||||||
|
public final SpringTestRule spring = new SpringTestRule(); |
||||||
|
|
||||||
|
@Autowired |
||||||
|
MockMvc mvc; |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenChangePasswordPageNotSetThenDefaultChangePasswordPageUsed() throws Exception { |
||||||
|
this.spring.register(PasswordManagementWithDefaultChangePasswordPageConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mvc.perform(get("/.well-known/change-password")).andExpect(status().isFound()) |
||||||
|
.andExpect(redirectedUrl("/change-password")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenChangePasswordPageSetThenSpecifiedChangePasswordPageUsed() throws Exception { |
||||||
|
this.spring.register(PasswordManagementWithCustomChangePasswordPageConfig.class).autowire(); |
||||||
|
|
||||||
|
this.mvc.perform(get("/.well-known/change-password")).andExpect(status().isFound()) |
||||||
|
.andExpect(redirectedUrl("/custom-change-password-page")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenSettingNullChangePasswordPage() { |
||||||
|
PasswordManagementConfigurer configurer = new PasswordManagementConfigurer(); |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> configurer.changePasswordPage(null)) |
||||||
|
.withMessage("changePasswordPage cannot be empty"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenSettingEmptyChangePasswordPage() { |
||||||
|
PasswordManagementConfigurer configurer = new PasswordManagementConfigurer(); |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> configurer.changePasswordPage("")) |
||||||
|
.withMessage("changePasswordPage cannot be empty"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenSettingBlankChangePasswordPage() { |
||||||
|
PasswordManagementConfigurer configurer = new PasswordManagementConfigurer(); |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> configurer.changePasswordPage(" ")) |
||||||
|
.withMessage("changePasswordPage cannot be empty"); |
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class PasswordManagementWithDefaultChangePasswordPageConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception { |
||||||
|
// @formatter:off
|
||||||
|
return http |
||||||
|
.passwordManagement(withDefaults()) |
||||||
|
.build(); |
||||||
|
// @formatter:on
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
static class PasswordManagementWithCustomChangePasswordPageConfig { |
||||||
|
|
||||||
|
@Bean |
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception { |
||||||
|
// @formatter:off
|
||||||
|
return http |
||||||
|
.passwordManagement((passwordManagement) -> passwordManagement |
||||||
|
.changePasswordPage("/custom-change-password-page") |
||||||
|
) |
||||||
|
.build(); |
||||||
|
// @formatter:on
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,65 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.http; |
||||||
|
|
||||||
|
import org.junit.Rule; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.security.config.test.SpringTestRule; |
||||||
|
import org.springframework.test.web.servlet.MockMvc; |
||||||
|
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; |
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; |
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link WellKnownChangePasswordBeanDefinitionParser}. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
*/ |
||||||
|
public class WellKnownChangePasswordBeanDefinitionParserTests { |
||||||
|
|
||||||
|
private static final String CONFIG_LOCATION_PREFIX = "classpath:org/springframework/security/config/http/WellKnownChangePasswordBeanDefinitionParserTests"; |
||||||
|
|
||||||
|
@Rule |
||||||
|
public final SpringTestRule spring = new SpringTestRule(); |
||||||
|
|
||||||
|
@Autowired |
||||||
|
MockMvc mvc; |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenChangePasswordPageNotSetThenDefaultChangePasswordPageUsed() throws Exception { |
||||||
|
this.spring.configLocations(xml("DefaultChangePasswordPage")).autowire(); |
||||||
|
|
||||||
|
this.mvc.perform(get("/.well-known/change-password")).andExpect(status().isFound()) |
||||||
|
.andExpect(redirectedUrl("/change-password")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenChangePasswordPageSetThenSpecifiedChangePasswordPageUsed() throws Exception { |
||||||
|
this.spring.configLocations(xml("CustomChangePasswordPage")).autowire(); |
||||||
|
|
||||||
|
this.mvc.perform(get("/.well-known/change-password")).andExpect(status().isFound()) |
||||||
|
.andExpect(redirectedUrl("/custom-change-password-page")); |
||||||
|
} |
||||||
|
|
||||||
|
private String xml(String configName) { |
||||||
|
return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml"; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,79 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.web.server; |
||||||
|
|
||||||
|
import org.apache.http.HttpHeaders; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder; |
||||||
|
import org.springframework.security.config.web.server.ServerHttpSecurity.PasswordManagementSpec; |
||||||
|
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder; |
||||||
|
import org.springframework.test.web.reactive.server.WebTestClient; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link PasswordManagementSpec}. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
*/ |
||||||
|
public class PasswordManagementSpecTests { |
||||||
|
|
||||||
|
ServerHttpSecurity http = ServerHttpSecurityConfigurationBuilder.httpWithDefaultAuthentication(); |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenChangePasswordPageNotSetThenDefaultChangePasswordPageUsed() { |
||||||
|
this.http.passwordManagement(); |
||||||
|
|
||||||
|
WebTestClient client = buildClient(); |
||||||
|
client.get().uri("/.well-known/change-password").exchange().expectStatus().isFound().expectHeader() |
||||||
|
.valueEquals(HttpHeaders.LOCATION, "/change-password"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenChangePasswordPageSetThenSpecifiedChangePasswordPageUsed() { |
||||||
|
this.http.passwordManagement( |
||||||
|
(passwordManagement) -> passwordManagement.changePasswordPage("/custom-change-password-page")); |
||||||
|
|
||||||
|
WebTestClient client = buildClient(); |
||||||
|
client.get().uri("/.well-known/change-password").exchange().expectStatus().isFound().expectHeader() |
||||||
|
.valueEquals(HttpHeaders.LOCATION, "/custom-change-password-page"); |
||||||
|
} |
||||||
|
|
||||||
|
private WebTestClient buildClient() { |
||||||
|
return WebTestClientBuilder.bindToWebFilters(this.http.build()).build(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenSettingNullChangePasswordPage() { |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> this.http.passwordManagement().changePasswordPage(null)) |
||||||
|
.withMessage("changePasswordPage cannot be empty"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenSettingEmptyChangePasswordPage() { |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> this.http.passwordManagement().changePasswordPage("")) |
||||||
|
.withMessage("changePasswordPage cannot be empty"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenSettingBlankChangePasswordPage() { |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> this.http.passwordManagement().changePasswordPage(" ")) |
||||||
|
.withMessage("changePasswordPage cannot be empty"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,97 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.web.server |
||||||
|
|
||||||
|
import org.apache.http.HttpHeaders |
||||||
|
import org.junit.Rule |
||||||
|
import org.junit.Test |
||||||
|
import org.springframework.beans.factory.annotation.Autowired |
||||||
|
import org.springframework.context.ApplicationContext |
||||||
|
import org.springframework.context.annotation.Bean |
||||||
|
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity |
||||||
|
import org.springframework.security.config.test.SpringTestRule |
||||||
|
import org.springframework.security.web.server.SecurityWebFilterChain |
||||||
|
import org.springframework.test.web.reactive.server.WebTestClient |
||||||
|
import org.springframework.web.reactive.config.EnableWebFlux |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for [ServerPasswordManagementDsl]. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
*/ |
||||||
|
class ServerPasswordManagementDslTests { |
||||||
|
|
||||||
|
@Rule |
||||||
|
@JvmField |
||||||
|
val spring = SpringTestRule() |
||||||
|
|
||||||
|
private lateinit var client: WebTestClient |
||||||
|
|
||||||
|
@Autowired |
||||||
|
fun setup(context: ApplicationContext) { |
||||||
|
this.client = WebTestClient |
||||||
|
.bindToApplicationContext(context) |
||||||
|
.configureClient() |
||||||
|
.build() |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun `when change password page not set then default change password page used`() { |
||||||
|
this.spring.register(PasswordManagementWithDefaultChangePasswordPageConfig::class.java).autowire() |
||||||
|
|
||||||
|
this.client.get() |
||||||
|
.uri("/.well-known/change-password") |
||||||
|
.exchange() |
||||||
|
.expectStatus().isFound |
||||||
|
.expectHeader().valueEquals(HttpHeaders.LOCATION, "/change-password") |
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebFluxSecurity |
||||||
|
@EnableWebFlux |
||||||
|
open class PasswordManagementWithDefaultChangePasswordPageConfig { |
||||||
|
@Bean |
||||||
|
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { |
||||||
|
return http { |
||||||
|
passwordManagement {} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun `when change password page set then specified change password page used`() { |
||||||
|
this.spring.register(PasswordManagementWithCustomChangePasswordPageConfig::class.java).autowire() |
||||||
|
|
||||||
|
this.client.get() |
||||||
|
.uri("/.well-known/change-password") |
||||||
|
.exchange() |
||||||
|
.expectStatus().isFound |
||||||
|
.expectHeader().valueEquals(HttpHeaders.LOCATION, "/custom-change-password-page") |
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebFluxSecurity |
||||||
|
@EnableWebFlux |
||||||
|
open class PasswordManagementWithCustomChangePasswordPageConfig { |
||||||
|
@Bean |
||||||
|
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { |
||||||
|
return http { |
||||||
|
passwordManagement { |
||||||
|
changePasswordPage = "/custom-change-password-page" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,84 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.web.servlet |
||||||
|
|
||||||
|
import org.junit.Rule |
||||||
|
import org.junit.Test |
||||||
|
import org.springframework.beans.factory.annotation.Autowired |
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity |
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity |
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter |
||||||
|
import org.springframework.security.config.test.SpringTestRule |
||||||
|
import org.springframework.test.web.servlet.MockMvc |
||||||
|
import org.springframework.test.web.servlet.get |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for [PasswordManagementDsl]. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
*/ |
||||||
|
class PasswordManagementDslTests { |
||||||
|
|
||||||
|
@Rule |
||||||
|
@JvmField |
||||||
|
val spring = SpringTestRule() |
||||||
|
|
||||||
|
@Autowired |
||||||
|
lateinit var mockMvc: MockMvc |
||||||
|
|
||||||
|
@Test |
||||||
|
fun `when change password page not set then default change password page used`() { |
||||||
|
this.spring.register(PasswordManagementWithDefaultChangePasswordPageConfig::class.java).autowire() |
||||||
|
|
||||||
|
this.mockMvc.get("/.well-known/change-password") |
||||||
|
.andExpect { |
||||||
|
status { isFound() } |
||||||
|
redirectedUrl("/change-password") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
open class PasswordManagementWithDefaultChangePasswordPageConfig : WebSecurityConfigurerAdapter() { |
||||||
|
override fun configure(http: HttpSecurity) { |
||||||
|
http { |
||||||
|
passwordManagement {} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
fun `when change password page set then specified change password page used`() { |
||||||
|
this.spring.register(PasswordManagementWithCustomChangePasswordPageConfig::class.java).autowire() |
||||||
|
|
||||||
|
this.mockMvc.get("/.well-known/change-password") |
||||||
|
.andExpect { |
||||||
|
status { isFound() } |
||||||
|
redirectedUrl("/custom-change-password-page") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@EnableWebSecurity |
||||||
|
open class PasswordManagementWithCustomChangePasswordPageConfig : WebSecurityConfigurerAdapter() { |
||||||
|
override fun configure(http: HttpSecurity) { |
||||||
|
http { |
||||||
|
passwordManagement { |
||||||
|
changePasswordPage = "/custom-change-password-page" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<!-- |
||||||
|
~ Copyright 2002-2021 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. |
||||||
|
--> |
||||||
|
|
||||||
|
<b:beans xmlns:b="http://www.springframework.org/schema/beans" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xmlns="http://www.springframework.org/schema/security" |
||||||
|
xsi:schemaLocation=" |
||||||
|
http://www.springframework.org/schema/security |
||||||
|
https://www.springframework.org/schema/security/spring-security.xsd |
||||||
|
http://www.springframework.org/schema/beans |
||||||
|
https://www.springframework.org/schema/beans/spring-beans.xsd"> |
||||||
|
|
||||||
|
<http auto-config="true"> |
||||||
|
<password-management change-password-page="/custom-change-password-page"/> |
||||||
|
</http> |
||||||
|
|
||||||
|
<b:import resource="userservice.xml"/> |
||||||
|
</b:beans> |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<!-- |
||||||
|
~ Copyright 2002-2021 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. |
||||||
|
--> |
||||||
|
|
||||||
|
<b:beans xmlns:b="http://www.springframework.org/schema/beans" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xmlns="http://www.springframework.org/schema/security" |
||||||
|
xsi:schemaLocation=" |
||||||
|
http://www.springframework.org/schema/security |
||||||
|
https://www.springframework.org/schema/security/spring-security.xsd |
||||||
|
http://www.springframework.org/schema/beans |
||||||
|
https://www.springframework.org/schema/beans/spring-beans.xsd"> |
||||||
|
|
||||||
|
<http auto-config="true"> |
||||||
|
<password-management/> |
||||||
|
</http> |
||||||
|
|
||||||
|
<b:import resource="userservice.xml"/> |
||||||
|
</b:beans> |
||||||
@ -0,0 +1,71 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.web; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import javax.servlet.FilterChain; |
||||||
|
import javax.servlet.ServletException; |
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
import org.springframework.web.filter.OncePerRequestFilter; |
||||||
|
|
||||||
|
/** |
||||||
|
* Filter that redirects requests that match {@link RequestMatcher} to the specified URL. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
* @since 5.6 |
||||||
|
*/ |
||||||
|
public final class RequestMatcherRedirectFilter extends OncePerRequestFilter { |
||||||
|
|
||||||
|
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); |
||||||
|
|
||||||
|
private final RequestMatcher requestMatcher; |
||||||
|
|
||||||
|
private final String redirectUrl; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create and initialize an instance of the filter. |
||||||
|
* @param requestMatcher the request matcher |
||||||
|
* @param redirectUrl the redirect URL |
||||||
|
*/ |
||||||
|
public RequestMatcherRedirectFilter(RequestMatcher requestMatcher, String redirectUrl) { |
||||||
|
Assert.notNull(requestMatcher, "requestMatcher cannot be null"); |
||||||
|
Assert.hasText(redirectUrl, "redirectUrl cannot be empty"); |
||||||
|
this.requestMatcher = requestMatcher; |
||||||
|
this.redirectUrl = redirectUrl; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) |
||||||
|
throws ServletException, IOException { |
||||||
|
|
||||||
|
if (this.requestMatcher.matches(request)) { |
||||||
|
this.redirectStrategy.sendRedirect(request, response, this.redirectUrl); |
||||||
|
} |
||||||
|
else { |
||||||
|
filterChain.doFilter(request, response); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,70 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.web.server; |
||||||
|
|
||||||
|
import java.net.URI; |
||||||
|
|
||||||
|
import reactor.core.publisher.Mono; |
||||||
|
|
||||||
|
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; |
||||||
|
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
import org.springframework.web.server.ServerWebExchange; |
||||||
|
import org.springframework.web.server.WebFilter; |
||||||
|
import org.springframework.web.server.WebFilterChain; |
||||||
|
|
||||||
|
/** |
||||||
|
* Web filter that redirects requests that match {@link ServerWebExchangeMatcher} to the |
||||||
|
* specified URL. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
* @since 5.6 |
||||||
|
*/ |
||||||
|
public final class ExchangeMatcherRedirectWebFilter implements WebFilter { |
||||||
|
|
||||||
|
private final ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy(); |
||||||
|
|
||||||
|
private final ServerWebExchangeMatcher exchangeMatcher; |
||||||
|
|
||||||
|
private final URI redirectUri; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create and initialize an instance of the web filter. |
||||||
|
* @param exchangeMatcher the exchange matcher |
||||||
|
* @param redirectUrl the redirect URL |
||||||
|
*/ |
||||||
|
public ExchangeMatcherRedirectWebFilter(ServerWebExchangeMatcher exchangeMatcher, String redirectUrl) { |
||||||
|
Assert.notNull(exchangeMatcher, "exchangeMatcher cannot be null"); |
||||||
|
Assert.hasText(redirectUrl, "redirectUrl cannot be empty"); |
||||||
|
this.exchangeMatcher = exchangeMatcher; |
||||||
|
this.redirectUri = URI.create(redirectUrl); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritDoc} |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { |
||||||
|
// @formatter:off
|
||||||
|
return this.exchangeMatcher.matches(exchange) |
||||||
|
.filter(MatchResult::isMatch) |
||||||
|
.switchIfEmpty(chain.filter(exchange).then(Mono.empty())) |
||||||
|
.flatMap((result) -> this.redirectStrategy.sendRedirect(exchange, this.redirectUri)); |
||||||
|
// @formatter:on
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,105 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.web; |
||||||
|
|
||||||
|
import javax.servlet.FilterChain; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus; |
||||||
|
import org.springframework.mock.web.MockHttpServletRequest; |
||||||
|
import org.springframework.mock.web.MockHttpServletResponse; |
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||||
|
import static org.mockito.Mockito.mock; |
||||||
|
import static org.mockito.Mockito.verify; |
||||||
|
import static org.mockito.Mockito.verifyNoInteractions; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link RequestMatcherRedirectFilter}. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
*/ |
||||||
|
public class RequestMatcherRedirectFilterTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void doFilterWhenRequestMatchThenRedirectToSpecifiedUrl() throws Exception { |
||||||
|
RequestMatcherRedirectFilter filter = new RequestMatcherRedirectFilter(new AntPathRequestMatcher("/context"), |
||||||
|
"/test"); |
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest(); |
||||||
|
request.setServletPath("/context"); |
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse(); |
||||||
|
FilterChain filterChain = mock(FilterChain.class); |
||||||
|
|
||||||
|
filter.doFilter(request, response, filterChain); |
||||||
|
|
||||||
|
assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value()); |
||||||
|
assertThat(response.getRedirectedUrl()).isEqualTo("/test"); |
||||||
|
|
||||||
|
verifyNoInteractions(filterChain); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void doFilterWhenRequestNotMatchThenNextFilter() throws Exception { |
||||||
|
RequestMatcherRedirectFilter filter = new RequestMatcherRedirectFilter(new AntPathRequestMatcher("/context"), |
||||||
|
"/test"); |
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest(); |
||||||
|
request.setServletPath("/test"); |
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse(); |
||||||
|
FilterChain filterChain = mock(FilterChain.class); |
||||||
|
|
||||||
|
filter.doFilter(request, response, filterChain); |
||||||
|
|
||||||
|
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); |
||||||
|
|
||||||
|
verify(filterChain).doFilter(request, response); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void constructWhenRequestMatcherNull() { |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> new RequestMatcherRedirectFilter(null, "/test")) |
||||||
|
.withMessage("requestMatcher cannot be null"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void constructWhenRedirectUrlNull() { |
||||||
|
assertThatIllegalArgumentException() |
||||||
|
.isThrownBy(() -> new RequestMatcherRedirectFilter(new AntPathRequestMatcher("/**"), null)) |
||||||
|
.withMessage("redirectUrl cannot be empty"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void constructWhenRedirectUrlEmpty() { |
||||||
|
assertThatIllegalArgumentException() |
||||||
|
.isThrownBy(() -> new RequestMatcherRedirectFilter(new AntPathRequestMatcher("/**"), "")) |
||||||
|
.withMessage("redirectUrl cannot be empty"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void constructWhenRedirectUrlBlank() { |
||||||
|
assertThatIllegalArgumentException() |
||||||
|
.isThrownBy(() -> new RequestMatcherRedirectFilter(new AntPathRequestMatcher("/**"), " ")) |
||||||
|
.withMessage("redirectUrl cannot be empty"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,87 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2021 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.web.server; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders; |
||||||
|
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher; |
||||||
|
import org.springframework.test.web.reactive.server.WebTestClient; |
||||||
|
import org.springframework.web.server.handler.FilteringWebHandler; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link ExchangeMatcherRedirectWebFilter}. |
||||||
|
* |
||||||
|
* @author Evgeniy Cheban |
||||||
|
*/ |
||||||
|
public class ExchangeMatcherRedirectWebFilterTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void filterWhenRequestMatchThenRedirectToSpecifiedUrl() { |
||||||
|
ExchangeMatcherRedirectWebFilter filter = new ExchangeMatcherRedirectWebFilter( |
||||||
|
new PathPatternParserServerWebExchangeMatcher("/context"), "/test"); |
||||||
|
FilteringWebHandler handler = new FilteringWebHandler((e) -> e.getResponse().setComplete(), |
||||||
|
Collections.singletonList(filter)); |
||||||
|
|
||||||
|
WebTestClient client = WebTestClient.bindToWebHandler(handler).build(); |
||||||
|
client.get().uri("/context").exchange().expectStatus().isFound().expectHeader() |
||||||
|
.valueEquals(HttpHeaders.LOCATION, "/test"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void filterWhenRequestNotMatchThenNextFilter() { |
||||||
|
ExchangeMatcherRedirectWebFilter filter = new ExchangeMatcherRedirectWebFilter( |
||||||
|
new PathPatternParserServerWebExchangeMatcher("/context"), "/test"); |
||||||
|
FilteringWebHandler handler = new FilteringWebHandler((e) -> e.getResponse().setComplete(), |
||||||
|
Collections.singletonList(filter)); |
||||||
|
|
||||||
|
WebTestClient client = WebTestClient.bindToWebHandler(handler).build(); |
||||||
|
client.get().uri("/test").exchange().expectStatus().isOk(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void constructWhenExchangeMatcherNull() { |
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> new ExchangeMatcherRedirectWebFilter(null, "/test")) |
||||||
|
.withMessage("exchangeMatcher cannot be null"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void constructWhenRedirectUrlNull() { |
||||||
|
assertThatIllegalArgumentException().isThrownBy( |
||||||
|
() -> new ExchangeMatcherRedirectWebFilter(new PathPatternParserServerWebExchangeMatcher("/**"), null)) |
||||||
|
.withMessage("redirectUrl cannot be empty"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void constructWhenRedirectUrlEmpty() { |
||||||
|
assertThatIllegalArgumentException().isThrownBy( |
||||||
|
() -> new ExchangeMatcherRedirectWebFilter(new PathPatternParserServerWebExchangeMatcher("/**"), "")) |
||||||
|
.withMessage("redirectUrl cannot be empty"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void constructWhenRedirectUrlBlank() { |
||||||
|
assertThatIllegalArgumentException().isThrownBy( |
||||||
|
() -> new ExchangeMatcherRedirectWebFilter(new PathPatternParserServerWebExchangeMatcher("/**"), " ")) |
||||||
|
.withMessage("redirectUrl cannot be empty"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue