Added change password page.
This commit is contained in:
parent
be9edf18e4
commit
506a474819
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
|||
</parent>
|
||||
<groupId>nl.andrewl</groupId>
|
||||
<artifactId>coyotecredit</artifactId>
|
||||
<version>1.3.1</version>
|
||||
<version>1.3.2</version>
|
||||
<name>coyotecredit</name>
|
||||
<description>Banking and stock trading application to teach students.</description>
|
||||
<properties>
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
package nl.andrewl.coyotecredit.ctl.user;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import nl.andrewl.coyotecredit.ctl.user.dto.ChangePasswordPayload;
|
||||
import nl.andrewl.coyotecredit.model.User;
|
||||
import nl.andrewl.coyotecredit.service.UserService;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(path = "/users/{userId}")
|
||||
|
@ -22,4 +20,16 @@ public class UserPage {
|
|||
model.addAttribute("user", userService.getUser(userId, user));
|
||||
return "user";
|
||||
}
|
||||
|
||||
@GetMapping(path = "/changePassword")
|
||||
public String getChangePasswordPage(@PathVariable long userId, @AuthenticationPrincipal User user) {
|
||||
userService.ensureCanChangePassword(user, userId);
|
||||
return "user/change_password";
|
||||
}
|
||||
|
||||
@PostMapping(path = "/changePassword")
|
||||
public String postChangePassword(@PathVariable long userId, @AuthenticationPrincipal User user, @ModelAttribute ChangePasswordPayload payload) {
|
||||
userService.changePassword(user, userId, payload);
|
||||
return "redirect:/users/" + userId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package nl.andrewl.coyotecredit.ctl.user.dto;
|
||||
|
||||
public record ChangePasswordPayload(
|
||||
String currentPassword,
|
||||
String newPassword
|
||||
) {}
|
|
@ -30,7 +30,7 @@ public class User implements UserDetails {
|
|||
@Column(nullable = false, unique = true)
|
||||
private String username;
|
||||
|
||||
@Column(nullable = false)
|
||||
@Column(nullable = false) @Setter
|
||||
private String passwordHash;
|
||||
|
||||
@Column(nullable = false)
|
||||
|
|
|
@ -3,6 +3,7 @@ package nl.andrewl.coyotecredit.service;
|
|||
import lombok.RequiredArgsConstructor;
|
||||
import nl.andrewl.coyotecredit.ctl.exchange.dto.InvitationData;
|
||||
import nl.andrewl.coyotecredit.ctl.dto.RegisterPayload;
|
||||
import nl.andrewl.coyotecredit.ctl.user.dto.ChangePasswordPayload;
|
||||
import nl.andrewl.coyotecredit.ctl.user.dto.UserData;
|
||||
import nl.andrewl.coyotecredit.ctl.user.dto.UserNotificationData;
|
||||
import nl.andrewl.coyotecredit.dao.*;
|
||||
|
@ -164,4 +165,31 @@ public class UserService {
|
|||
}
|
||||
notificationRepository.saveAll(notifications);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public void ensureCanChangePassword(User user, long userId) {
|
||||
User changingUser = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||
if (!(user.isAdmin() || user.getId().equals(changingUser.getId()))) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void changePassword(User user, long userId, ChangePasswordPayload payload) {
|
||||
User changingUser = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||
if (!(user.isAdmin() || user.getId().equals(changingUser.getId()))) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
if (!passwordEncoder.matches(payload.currentPassword(), changingUser.getPasswordHash())) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Current password is incorrect.");
|
||||
}
|
||||
if (payload.currentPassword().equals(payload.newPassword())) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "New password is the same as the current password.");
|
||||
}
|
||||
changingUser.setPasswordHash(passwordEncoder.encode(payload.newPassword()));
|
||||
userRepository.save(changingUser);
|
||||
notificationRepository.save(new UserNotification(changingUser, "Your password has just been updated."));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html
|
||||
lang="en"
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
th:replace="~{layout/basic_page :: layout (title='Exchange Accounts', content=~{::#content})}"
|
||||
th:replace="~{layout/basic_page :: layout (title='Create Account', content=~{::#content})}"
|
||||
>
|
||||
<div id="content" class="container">
|
||||
<h1 class="display-4">Create Account</h1>
|
||||
|
|
|
@ -62,4 +62,8 @@
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a class="btn btn-primary" th:href="@{/users/{uId}/changePassword(uId=${userId})}">Change Password</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html
|
||||
lang="en"
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
th:replace="~{layout/basic_page :: layout (title='Change Password', content=~{::#content})}"
|
||||
>
|
||||
<div id="content" class="container">
|
||||
<h1 class="display-4">Change Password</h1>
|
||||
<p class="lead">
|
||||
Change the password to your account.
|
||||
</p>
|
||||
<form th:action="@{/users/{uId}/changePassword(uId=${userId})}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="currentPasswordInput" class="form-label">Current Password</label>
|
||||
<input id="currentPasswordInput" name="currentPassword" class="form-control" type="password" required/>
|
||||
<small class="text-muted">You must enter your current password to confirm your access to the account.</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="newPasswordInput" class="form-label">New Password</label>
|
||||
<input id="newPasswordInput" name="newPassword" class="form-control" type="password" minlength="6" required/>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
Loading…
Reference in New Issue