Added change password page.
This commit is contained in:
parent
be9edf18e4
commit
506a474819
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>nl.andrewl</groupId>
|
<groupId>nl.andrewl</groupId>
|
||||||
<artifactId>coyotecredit</artifactId>
|
<artifactId>coyotecredit</artifactId>
|
||||||
<version>1.3.1</version>
|
<version>1.3.2</version>
|
||||||
<name>coyotecredit</name>
|
<name>coyotecredit</name>
|
||||||
<description>Banking and stock trading application to teach students.</description>
|
<description>Banking and stock trading application to teach students.</description>
|
||||||
<properties>
|
<properties>
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
package nl.andrewl.coyotecredit.ctl.user;
|
package nl.andrewl.coyotecredit.ctl.user;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import nl.andrewl.coyotecredit.ctl.user.dto.ChangePasswordPayload;
|
||||||
import nl.andrewl.coyotecredit.model.User;
|
import nl.andrewl.coyotecredit.model.User;
|
||||||
import nl.andrewl.coyotecredit.service.UserService;
|
import nl.andrewl.coyotecredit.service.UserService;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping(path = "/users/{userId}")
|
@RequestMapping(path = "/users/{userId}")
|
||||||
|
@ -22,4 +20,16 @@ public class UserPage {
|
||||||
model.addAttribute("user", userService.getUser(userId, user));
|
model.addAttribute("user", userService.getUser(userId, user));
|
||||||
return "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)
|
@Column(nullable = false, unique = true)
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false) @Setter
|
||||||
private String passwordHash;
|
private String passwordHash;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package nl.andrewl.coyotecredit.service;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import nl.andrewl.coyotecredit.ctl.exchange.dto.InvitationData;
|
import nl.andrewl.coyotecredit.ctl.exchange.dto.InvitationData;
|
||||||
import nl.andrewl.coyotecredit.ctl.dto.RegisterPayload;
|
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.UserData;
|
||||||
import nl.andrewl.coyotecredit.ctl.user.dto.UserNotificationData;
|
import nl.andrewl.coyotecredit.ctl.user.dto.UserNotificationData;
|
||||||
import nl.andrewl.coyotecredit.dao.*;
|
import nl.andrewl.coyotecredit.dao.*;
|
||||||
|
@ -164,4 +165,31 @@ public class UserService {
|
||||||
}
|
}
|
||||||
notificationRepository.saveAll(notifications);
|
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
|
<html
|
||||||
lang="en"
|
lang="en"
|
||||||
xmlns:th="http://www.thymeleaf.org"
|
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">
|
<div id="content" class="container">
|
||||||
<h1 class="display-4">Create Account</h1>
|
<h1 class="display-4">Create Account</h1>
|
||||||
|
|
|
@ -62,4 +62,8 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a class="btn btn-primary" th:href="@{/users/{uId}/changePassword(uId=${userId})}">Change Password</a>
|
||||||
|
</div>
|
||||||
</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