diff --git a/pom.xml b/pom.xml index 15851ec3b..edaf48b00 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.owasp.webgoat webgoat - 2023.3 + 2023.4-SNAPSHOT jar WebGoat @@ -675,7 +675,7 @@ ${project.build.directory}/webgoat-${project.version}.jar false - http://localhost:${webgoat.port}/WebGoat/ + http://localhost:${webgoat.port}/WebGoat/actuator/health diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java index ace84be78..4dacce209 100644 --- a/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java @@ -57,6 +57,7 @@ import org.springframework.web.servlet.ModelAndView; }) public class ResetLinkAssignment extends AssignmentEndpoint { + private static final String VIEW_FORMATTER = "lessons/passwordreset/templates/%s.html"; static final String PASSWORD_TOM_9 = "somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom"; static final String TOM_EMAIL = "tom@webgoat-cloud.org"; @@ -65,15 +66,18 @@ public class ResetLinkAssignment extends AssignmentEndpoint { static List resetLinks = new ArrayList<>(); static final String TEMPLATE = - "Hi, you requested a password reset link, please use this link to reset your" - + " password.\n" - + " \n\n" - + "If you did not request this password change you can ignore this message.\n" - + "If you have any comments or questions, please do not hesitate to reach us at" - + " support@webgoat-cloud.org\n\n" - + "Kind regards, \n" - + "Team WebGoat"; + """ + Hi, you requested a password reset link, please use this link to reset your + password. + + If you did not request this password change you can ignore this message. + If you have any comments or questions, please do not hesitate to reach us at + support@webgoat-cloud.org + + Kind regards, + Team WebGoat + """; @PostMapping("/PasswordReset/reset/login") @ResponseBody @@ -98,20 +102,14 @@ public class ResetLinkAssignment extends AssignmentEndpoint { form.setResetLink(link); model.addAttribute("form", form); modelAndView.addObject("form", form); - modelAndView.setViewName("password_reset"); // Display html page for changing password + modelAndView.setViewName( + VIEW_FORMATTER.formatted("password_reset")); // Display html page for changing password } else { - modelAndView.setViewName("password_link_not_found"); + modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found")); } return modelAndView; } - @GetMapping("/PasswordReset/reset/change-password") - public ModelAndView illegalCall() { - ModelAndView modelAndView = new ModelAndView(); - modelAndView.setViewName("password_link_not_found"); - return modelAndView; - } - @PostMapping("/PasswordReset/reset/change-password") public ModelAndView changePassword( @ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) { @@ -120,17 +118,17 @@ public class ResetLinkAssignment extends AssignmentEndpoint { bindingResult.rejectValue("password", "not.empty"); } if (bindingResult.hasErrors()) { - modelAndView.setViewName("password_reset"); + modelAndView.setViewName(VIEW_FORMATTER.formatted("password_reset")); return modelAndView; } if (!resetLinks.contains(form.getResetLink())) { - modelAndView.setViewName("password_link_not_found"); + modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found")); return modelAndView; } if (checkIfLinkIsFromTom(form.getResetLink())) { usersToTomPassword.put(getWebSession().getUserName(), form.getPassword()); } - modelAndView.setViewName("lessons/passwordreset/templates/success.html"); + modelAndView.setViewName(VIEW_FORMATTER.formatted("success")); return modelAndView; } diff --git a/src/test/java/org/owasp/webgoat/container/plugins/LessonTest.java b/src/test/java/org/owasp/webgoat/container/plugins/LessonTest.java index 465046c67..6d7ec5ed2 100644 --- a/src/test/java/org/owasp/webgoat/container/plugins/LessonTest.java +++ b/src/test/java/org/owasp/webgoat/container/plugins/LessonTest.java @@ -18,7 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.context.WebApplicationContext; diff --git a/src/test/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentTest.java b/src/test/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentTest.java new file mode 100644 index 000000000..6010c432d --- /dev/null +++ b/src/test/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentTest.java @@ -0,0 +1,103 @@ +package org.owasp.webgoat.lessons.passwordreset; + +import static org.mockito.Mockito.when; +import static org.owasp.webgoat.lessons.passwordreset.ResetLinkAssignment.TOM_EMAIL; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.owasp.webgoat.container.plugins.LessonTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ResourceLoader; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +@ExtendWith(SpringExtension.class) +class ResetLinkAssignmentTest extends LessonTest { + + @Value("${webwolf.host}") + private String webWolfHost; + + @Value("${webwolf.port}") + private String webWolfPort; + + @Autowired private ResourceLoader resourceLoader; + + @BeforeEach + public void setup() { + when(webSession.getCurrentLesson()).thenReturn(new PasswordReset()); + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + } + + @Test + void wrongResetLink() throws Exception { + MvcResult mvcResult = + mockMvc + .perform( + MockMvcRequestBuilders.get("/PasswordReset/reset/reset-password/{link}", "test")) + .andExpect(status().isOk()) + .andExpect(view().name("lessons/passwordreset/templates/password_link_not_found.html")) + .andReturn(); + Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName())) + .isNotNull(); + } + + @Test + void changePasswordWithoutPasswordShouldReturnPasswordForm() throws Exception { + MvcResult mvcResult = + mockMvc + .perform(MockMvcRequestBuilders.post("/PasswordReset/reset/change-password")) + .andExpect(status().isOk()) + .andExpect(view().name("lessons/passwordreset/templates/password_reset.html")) + .andReturn(); + Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName())) + .isNotNull(); + } + + @Test + void changePasswordWithoutLinkShouldReturnPasswordLinkNotFound() throws Exception { + MvcResult mvcResult = + mockMvc + .perform( + MockMvcRequestBuilders.post("/PasswordReset/reset/change-password") + .param("password", "new_password")) + .andExpect(status().isOk()) + .andExpect(view().name("lessons/passwordreset/templates/password_link_not_found.html")) + .andReturn(); + Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName())) + .isNotNull(); + } + + @Test + void knownLinkShouldReturnPasswordResetPage() throws Exception { + // Create a reset link + mockMvc + .perform( + MockMvcRequestBuilders.post("/PasswordReset/ForgotPassword/create-password-reset-link") + .param("email", TOM_EMAIL) + .header(HttpHeaders.HOST, webWolfHost + ":" + webWolfPort)) + .andExpect(status().isOk()); + Assertions.assertThat(ResetLinkAssignment.resetLinks).isNotEmpty(); + + // With a known link you should be + MvcResult mvcResult = + mockMvc + .perform( + MockMvcRequestBuilders.get( + "/PasswordReset/reset/reset-password/{link}", + ResetLinkAssignment.resetLinks.get(0))) + .andExpect(status().isOk()) + .andExpect(view().name("lessons/passwordreset/templates/password_reset.html")) + .andReturn(); + + Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName())) + .isNotNull(); + } +}