1. Overview
In this quick article we’ll implement a simple “Change my own password” functionality available to the user after they register and log in.
2. Client Side – Change My Password Page
Let’s take a look at the very simple client side page:
<html>
<body>
<div id="errormsg" style="display:none"></div>
<div>
<input id="oldpass" name="oldpassword" type="password" />
<input id="pass" name="password" type="password" />
<input id="passConfirm" type="password" />
<span id="error" style="display:none">Password mismatch</span>
<button type="submit" onclick="savePass()">Change Password</button>
</div>
<script src="jquery.min.js"></script>
<script type="text/javascript">
var serverContext = [[@{/}]];
function savePass(){
var pass = $("#pass").val();
var valid = pass == $("#passConfirm").val();
if(!valid) {
$("#error").show();
return;
}
$.post(serverContext + "user/updatePassword",
{password: pass, oldpassword: $("#oldpass").val()} ,function(data){
window.location.href = serverContext +"/home.html?message="+data.message;
})
.fail(function(data) {
$("#errormsg").show().html(data.responseJSON.message);
});
}
</script>
</body>
</html>
3. Update User Password
Let’s now implement the server side operation as well:
@PostMapping("/user/updatePassword")
@PreAuthorize("hasRole('READ_PRIVILEGE')")
public GenericResponse changeUserPassword(Locale locale,
@RequestParam("password") String password,
@RequestParam("oldpassword") String oldPassword) {
User user = userService.findUserByEmail(
SecurityContextHolder.getContext().getAuthentication().getName());
if (!userService.checkIfValidOldPassword(user, oldPassword)) {
throw new InvalidOldPasswordException();
}
userService.changeUserPassword(user, password);
return new GenericResponse(messages.getMessage("message.updatePasswordSuc", null, locale));
}
Notice how the method is secured via the @PreAuthorize annotation, since it should only accessible to logged in users.
4. API Tests
Finally, let’s consume the API with some API tests to make sure everything is working fine; we’ll start with the simple configuration of the test and the data initialization:
@ExtendWith(SpringExtension.class)
@ContextConfiguration(
classes = { ConfigTest.class, PersistenceJPAConfig.class },
loader = AnnotationConfigContextLoader.class)
public class ChangePasswordApiTest {
private final String URL_PREFIX = "http://localhost:8080/";
private final String URL = URL_PREFIX + "/user/updatePassword";
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
FormAuthConfig formConfig = new FormAuthConfig(
URL_PREFIX + "/login", "username", "password");
@BeforeEach
public void init() {
User user = userRepository.findByEmail("[email protected]");
if (user == null) {
user = new User();
user.setFirstName("Test");
user.setLastName("Test");
user.setPassword(passwordEncoder.encode("test"));
user.setEmail("[email protected]");
user.setEnabled(true);
userRepository.save(user);
} else {
user.setPassword(passwordEncoder.encode("test"));
userRepository.save(user);
}
}
}
Now – let’s try to change password for a logged in user:
@Test
public void givenLoggedInUser_whenChangingPassword_thenCorrect() {
RequestSpecification request = RestAssured.given().auth()
.form("[email protected]", "test", formConfig);
Map<String, String> params = new HashMap<String, String>();
params.put("oldpassword", "test");
params.put("password", "newtest");
Response response = request.with().params(params).post(URL);
assertEquals(200, response.statusCode());
assertTrue(response.body().asString().contains("Password updated successfully"));
}
Next – let’s try to change password given a wrong old password:
@Test
public void givenWrongOldPassword_whenChangingPassword_thenBadRequest() {
RequestSpecification request = RestAssured.given().auth()
.form("[email protected]", "test", formConfig);
Map<String, String> params = new HashMap<String, String>();
params.put("oldpassword", "abc");
params.put("password", "newtest");
Response response = request.with().params(params).post(URL);
assertEquals(400, response.statusCode());
assertTrue(response.body().asString().contains("Invalid Old Password"));
}
Finally – let’s try to change password without authentication:
@Test
public void givenNotAuthenticatedUser_whenChangingPassword_thenRedirect() {
Map<String, String> params = new HashMap<String, String>();
params.put("oldpassword", "abc");
params.put("password", "xyz");
Response response = RestAssured.with().params(params).post(URL);
assertEquals(302, response.statusCode());
assertFalse(response.body().asString().contains("Password updated successfully"));
}
Note how – for each test – we’re providing a FormAuthConfig to handle the authentication.
We’re also resetting the password via init() to make sure we use the correct password before test.
5. Conclusion
And that’s a wrap – a straightforward way to allow the user to change their own password after registering and logging into the application.
The full implementation of this tutorial can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.