Improved validation, enforced uniqueness on usernames

This commit is contained in:
neviyn 2021-04-04 23:57:28 +01:00
parent db6282eec6
commit d5f7f4f53c
6 changed files with 104 additions and 54 deletions

View File

@ -52,6 +52,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<artifactId>spring-boot-starter-validation</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>

View File

@ -13,12 +13,19 @@ import javax.persistence.JoinTable
import javax.persistence.ManyToMany
import javax.persistence.ManyToOne
import javax.persistence.OneToMany
import javax.validation.constraints.Email
import javax.validation.constraints.NotBlank
@Entity
open class User(
@field:NotBlank(message = "Username is required")
@Column(unique = true)
open var username: String = "INVALID",
@field:NotBlank(message = "Email address is required")
@field:Email(message = "Email address invalid")
open var email: String = "INVALID",
@JsonIgnore
@field:NotBlank(message = "Password is required")
open var password: String = "INVALID",
@ManyToMany(cascade = [CascadeType.ALL])
@JsonIgnore
@ -29,7 +36,11 @@ open class User(
)
open var projects: MutableSet<Project> = mutableSetOf(),
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) open var id: Long? = null
)
) {
override fun toString(): String {
return "User(username='$username', email='$email', password='$password', id=$id)"
}
}
@Entity
open class Project(

View File

@ -8,6 +8,7 @@ import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.ui.Model
import org.springframework.validation.BindingResult
import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
@ -18,6 +19,7 @@ import org.springframework.web.server.ResponseStatusException
import java.time.Instant
import javax.persistence.EntityManager
import javax.transaction.Transactional
import javax.validation.Valid
@Controller
@ -37,7 +39,8 @@ class HtmlController @Autowired constructor(val userRepository: UserRepository,
}
@PostMapping("/register")
fun register(@ModelAttribute newUser: User) : String {
fun register(@Valid @ModelAttribute("user_details") newUser: User, bindingResult: BindingResult): String {
if (bindingResult.hasErrors()) return "register"
newUser.password = passwordEncoder().encode(newUser.password)
userRepository.save(newUser)
return "login"
@ -59,15 +62,14 @@ class HtmlController @Autowired constructor(val userRepository: UserRepository,
@PostMapping("/profile")
@Transactional
fun updateLoggedInUser(@ModelAttribute userData: DisplayUser, @AuthenticationPrincipal userDetails: CustomUserDetails, model: Model) : String {
if(userData.id == userDetails.user.id!! && passwordEncoder().matches(userData.oldPassword, userDetails.password)) {
fun updateLoggedInUser(@Valid @ModelAttribute userData: DisplayUser, @AuthenticationPrincipal userDetails: CustomUserDetails, model: Model): String {
if (userData.id == userDetails.user.id!! && passwordEncoder().matches(userData.oldPassword, userDetails.password)) {
val user = userDetails.user
user.email = userData.email
if(userData.password.isNotEmpty()) user.password = passwordEncoder().encode(userData.password)
if (userData.password.isNotEmpty()) user.password = passwordEncoder().encode(userData.password)
userRepository.save(user)
model.addAttribute("message", "Your profile has been updated")
}
else{
} else {
model.addAttribute("error", "Incorrect existing password")
}
model.addAttribute("userData", DisplayUser(userData.id, userData.username, userData.email, userData.password, ""))

View File

@ -1,17 +1,46 @@
package uk.co.neviyn.projectplanner
import java.time.Instant
import javax.validation.constraints.NotBlank
import javax.validation.constraints.PastOrPresent
import javax.validation.constraints.Positive
data class DisplayUser(val id: Long, val username: String, val email: String, val password: String, val oldPassword: String)
data class DisplayUser(
@field:Positive val id: Long,
val username: String,
val email: String,
val password: String,
@field:NotBlank val oldPassword: String
)
data class NewProject(val title: String)
data class NewProject(
@field:NotBlank val title: String
)
data class NewEvent(val title: String, val description: String, val start: Instant, val end: Instant)
data class NewEvent(
@field:NotBlank val title: String,
val description: String,
val start: Instant,
val end: Instant
)
data class EditedEvent(val id: Long, val title: String, val description: String, val start: Instant, val end: Instant)
data class EditedEvent(
@field:Positive val id: Long,
val title: String,
val description: String,
val start: Instant,
val end: Instant)
data class EventID(val id: Long)
data class EventID(
@field:Positive val id: Long
)
data class FlatComment(val created: Instant, val comment: String, val username: String)
data class FlatComment(
@field:PastOrPresent val created: Instant,
@field:NotBlank val comment: String,
@field:NotBlank val username: String
)
data class NewComment(val comment: String)
data class NewComment(
@field:NotBlank val comment: String
)

View File

@ -3,25 +3,51 @@ create schema if not exists projectplanner;
create table if not exists projectplanner."user"
(
id bigserial not null
constraint user_pk
primary key,
username varchar(50) not null,
email varchar(255) not null,
password varchar(255) not null
);
constraint
user_pk
primary
key,
username
varchar
(
50
) not null,
email varchar
(
255
) not null,
password varchar
(
255
) not null
);
create unique index if not exists user_id_uindex
create
unique index if not exists user_id_uindex
on projectplanner."user" (id);
create
unique index if not exists user_username_uindex
ON projectplanner."user" (username);
create table if not exists projectplanner.project
(
id bigserial not null
constraint project_pk
primary key,
title text not null
id
bigserial
not
null
constraint
project_pk
primary
key,
title
text
not
null
);
create unique index if not exists project_id_uindex
create
unique index if not exists project_id_uindex
on projectplanner.project (id);
create table if not exists projectplanner.team

View File

@ -12,30 +12,29 @@
</div>
<div class="row justify-content-center mt-3">
<div class="col-6 text-center">
<form class="needs-validation" method="post" novalidate th:action="@{/register}"
<form method="post" novalidate th:action="@{/register}"
th:object="${user_details}">
<div class="mb-3">
<label class="form-label" for="emailInput">Email address</label>
<input class="form-control form-control-lg" id="emailInput" required th:field="*{email}"
type="email"/>
<div class="invalid-feedback">
Please enter an email address.
<div class="text-danger" th:errors="*{email}" th:if="${#fields.hasErrors('email')}">Email Error
</div>
</div>
<div class="mb-3">
<label class="form-label" for="usernameInput">Username</label>
<input class="form-control form-control-lg" id="usernameInput" required th:field="*{username}"
type="text"/>
<div class="invalid-feedback">
Please enter a username.
<div class="text-danger" th:errors="*{username}" th:if="${#fields.hasErrors('username')}">Username
Error
</div>
</div>
<div class="mb-3">
<label class="form-label" for="passwordInput">Password</label>
<input class="form-control form-control-lg" id="passwordInput" required
th:field="*{password}" type="password"/>
<div class="invalid-feedback">
Please enter a password.
<div class="text-danger" th:errors="*{password}" th:if="${#fields.hasErrors('password')}">Password
Error
</div>
</div>
<button class="btn btn-primary btn-lg" type="submit">Submit</button>
@ -43,26 +42,5 @@
</div>
</div>
</div>
<script>
(function () {
'use strict'
// Fetch all the forms we want to apply custom Bootstrap validation styles to
let forms = document.querySelectorAll('.needs-validation')
// Loop over them and prevent submission
Array.prototype.slice.call(forms)
.forEach(function (form) {
form.addEventListener('submit', function (event) {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}
form.classList.add('was-validated')
}, false)
})
})()
</script>
</body>
</html>