Support A Well-Known URL for Changing Passwords

Closes gh-8657
This commit is contained in:
Evgeniy Cheban
2020-06-12 18:00:51 +03:00
committed by Josh Cummings
parent 85e95719a0
commit d121ab9565
25 changed files with 1307 additions and 4 deletions

View File

@@ -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
}
}
}

View File

@@ -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";
}
}

View File

@@ -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");
}
}