Improved validation, enforced uniqueness on usernames
This commit is contained in:
parent
db6282eec6
commit
d5f7f4f53c
4
pom.xml
4
pom.xml
@ -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>
|
||||
|
@ -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(
|
||||
|
@ -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, ""))
|
||||
|
@ -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
|
||||
)
|
@ -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
|
||||
|
@ -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>
|
Loading…
Reference in New Issue
Block a user