Update html layout
- add messages fragment
- extract error and success message list from edit.html
- extract error message list from create.html
- add messages.html
- update edit account
- update AccountController
- update postEditAccount for validation
- update getEditAccount for roleGroups
- update validation for AccountForm for edit
- add EditGroup
- update Account.Response.toAccountForm()
- update edit.html
- update create account
- update AccountController
- update postCreateAccount for validation
- update getCreateAccount for role group
- add validation to AccountForm
- add PasswordMatchValidator
- add annotation PasswordMatch
- add CreateGroup
- add temporary getRoles() in AccountRegistryService
- update AccountForm.toAccountRequest() in Mapping
- change management.css
- add ::selection
- add Selected Option Styling
- add passwordMatchCheck to management.js
- update create.html
- update user
- add makeLocalTime in management.js
- update users.html
- update Pagination
- add size of items
- rename size to show
- update goodbye.html
- update logout.html
- update login.html
- update welcome.html
- update index.html
- update layout
- update management.css
- update management.js
- update layout.html
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
package ltd.hlaeja.controller
|
||||
|
||||
import java.util.UUID
|
||||
import ltd.hlaeja.controller.validation.CreateGroup
|
||||
import ltd.hlaeja.controller.validation.EditGroup
|
||||
import ltd.hlaeja.dto.Pagination
|
||||
import ltd.hlaeja.exception.NoChangeException
|
||||
import ltd.hlaeja.exception.NotFoundException
|
||||
import ltd.hlaeja.exception.PasswordException
|
||||
import ltd.hlaeja.exception.UsernameDuplicateException
|
||||
import ltd.hlaeja.form.AccountForm
|
||||
import ltd.hlaeja.service.AccountRegistryService
|
||||
@@ -12,6 +13,8 @@ import ltd.hlaeja.util.toAccountForm
|
||||
import ltd.hlaeja.util.toAccountRequest
|
||||
import org.springframework.stereotype.Controller
|
||||
import org.springframework.ui.Model
|
||||
import org.springframework.validation.BindingResult
|
||||
import org.springframework.validation.annotation.Validated
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.ModelAttribute
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
@@ -36,6 +39,7 @@ class AccountController(
|
||||
): Mono<String> = accountRegistryService.getAccount(account)
|
||||
.doOnNext {
|
||||
model.addAttribute("account", account)
|
||||
model.addAttribute("roleGroups", accountRegistryService.getRoles())
|
||||
model.addAttribute("accountForm", it.toAccountForm())
|
||||
}
|
||||
.then(Mono.just("account/edit"))
|
||||
@@ -43,65 +47,93 @@ class AccountController(
|
||||
@PostMapping("/edit-{account}")
|
||||
fun postEditAccount(
|
||||
@PathVariable account: UUID,
|
||||
@ModelAttribute("accountForm") accountForm: AccountForm,
|
||||
@Validated(EditGroup::class) @ModelAttribute("accountForm") accountForm: AccountForm,
|
||||
bindingResult: BindingResult,
|
||||
model: Model,
|
||||
): Mono<String> = Mono.just(accountForm)
|
||||
.flatMap {
|
||||
accountRegistryService.updateAccount(
|
||||
account,
|
||||
it.toAccountRequest { password -> if (password.isNullOrEmpty()) null else password },
|
||||
)
|
||||
}
|
||||
.doOnNext {
|
||||
model.addAttribute("successMessage", "Saved changes!!!")
|
||||
model.addAttribute("account", account)
|
||||
model.addAttribute("accountForm", it.toAccountForm())
|
||||
}
|
||||
.then(Mono.just("account/edit"))
|
||||
.onErrorResume { error ->
|
||||
val errorMessage = when (error) {
|
||||
is NoChangeException -> Pair("successMessage", "No change to save")
|
||||
is NotFoundException -> Pair("errorMessage", "User dont exists. how did this happen?")
|
||||
is UsernameDuplicateException -> Pair("errorMessage", "Username already exists. Please choose another.")
|
||||
else -> Pair("errorMessage", "An unexpected error occurred. Please try again later.")
|
||||
): Mono<String> {
|
||||
val validationErrors = if (bindingResult.hasErrors()) {
|
||||
bindingResult.allErrors.map { error ->
|
||||
error.defaultMessage ?: "Unknown validation error"
|
||||
}
|
||||
model.addAttribute(errorMessage.first, errorMessage.second)
|
||||
model.addAttribute("accountForm", accountForm)
|
||||
model.addAttribute("account", account)
|
||||
Mono.just("account/edit")
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("accountForm", accountForm)
|
||||
model.addAttribute("validationErrors", validationErrors)
|
||||
model.addAttribute("roleGroups", accountRegistryService.getRoles())
|
||||
return Mono.just("account/edit")
|
||||
}
|
||||
|
||||
return Mono.just(accountForm)
|
||||
.flatMap { accountRegistryService.updateAccount(account, it.toAccountRequest()) }
|
||||
.doOnNext {
|
||||
model.addAttribute("successMessage", listOf("Saved changes!!!"))
|
||||
model.addAttribute("account", account)
|
||||
model.addAttribute("accountForm", it.toAccountForm())
|
||||
model.addAttribute("roleGroups", accountRegistryService.getRoles())
|
||||
}
|
||||
.then(Mono.just("account/edit"))
|
||||
.onErrorResume { error ->
|
||||
val errorMessage = when (error) {
|
||||
is NoChangeException -> Pair("successMessage", "No change to save.")
|
||||
is NotFoundException -> Pair("validationErrors", "User dont exists. how did this happen?")
|
||||
is UsernameDuplicateException -> Pair(
|
||||
"validationErrors",
|
||||
"Username already exists. Please choose another.",
|
||||
)
|
||||
else -> Pair("validationErrors", "An unexpected error occurred. Please try again later.")
|
||||
}
|
||||
|
||||
model.addAttribute(errorMessage.first, listOf(errorMessage.second))
|
||||
model.addAttribute("account", account)
|
||||
model.addAttribute("accountForm", accountForm)
|
||||
model.addAttribute("roleGroups", accountRegistryService.getRoles())
|
||||
Mono.just("account/edit")
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/create")
|
||||
fun getCreateAccount(
|
||||
model: Model,
|
||||
): Mono<String> = Mono.just("account/create")
|
||||
.doOnNext { model.addAttribute("accountForm", AccountForm("", "")) }
|
||||
.doOnNext {
|
||||
model.addAttribute("accountForm", AccountForm("", emptyList()))
|
||||
model.addAttribute("roleGroups", accountRegistryService.getRoles())
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
fun postCreateAccount(
|
||||
@ModelAttribute("accountForm") accountForm: AccountForm,
|
||||
@Validated(CreateGroup::class) @ModelAttribute("accountForm") accountForm: AccountForm,
|
||||
bindingResult: BindingResult,
|
||||
model: Model,
|
||||
): Mono<String> = Mono.just(accountForm)
|
||||
.flatMap {
|
||||
accountRegistryService.addAccount(
|
||||
it.toAccountRequest { password ->
|
||||
when {
|
||||
password.isNullOrEmpty() -> throw PasswordException("Password requirements failed")
|
||||
else -> password
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
.map { "redirect:/account" }
|
||||
.onErrorResume { error ->
|
||||
val errorMessage = when (error) {
|
||||
is UsernameDuplicateException -> "Username already exists. Please choose another."
|
||||
is PasswordException -> error.message
|
||||
else -> "An unexpected error occurred. Please try again later."
|
||||
): Mono<String> {
|
||||
val validationErrors = if (bindingResult.hasErrors()) {
|
||||
bindingResult.allErrors.map { error ->
|
||||
error.defaultMessage ?: "Unknown validation error"
|
||||
}
|
||||
model.addAttribute("errorMessage", errorMessage)
|
||||
Mono.just("account/create")
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("accountForm", accountForm)
|
||||
model.addAttribute("validationErrors", validationErrors)
|
||||
model.addAttribute("roleGroups", accountRegistryService.getRoles())
|
||||
return Mono.just("account/create")
|
||||
}
|
||||
return Mono.just(accountForm)
|
||||
.flatMap { accountRegistryService.addAccount(it.toAccountRequest()) }
|
||||
.map { "redirect:/account" }
|
||||
.onErrorResume { error ->
|
||||
val errorMessage = when (error) {
|
||||
is UsernameDuplicateException -> "Username already exists. Please choose another."
|
||||
else -> "An unexpected error occurred. Please try again later."
|
||||
}
|
||||
model.addAttribute("validationErrors", listOf(errorMessage))
|
||||
model.addAttribute("roleGroups", accountRegistryService.getRoles())
|
||||
Mono.just("account/create")
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
fun getDefaultAccounts(
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package ltd.hlaeja.controller.validation
|
||||
|
||||
interface CreateGroup
|
||||
@@ -0,0 +1,3 @@
|
||||
package ltd.hlaeja.controller.validation
|
||||
|
||||
interface EditGroup
|
||||
@@ -0,0 +1,15 @@
|
||||
package ltd.hlaeja.controller.validation
|
||||
|
||||
import jakarta.validation.Constraint
|
||||
import jakarta.validation.Payload
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@MustBeDocumented
|
||||
@Constraint(validatedBy = [PasswordMatchValidator::class])
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class PasswordMatch(
|
||||
val message: String = "Passwords must match",
|
||||
val groups: Array<KClass<*>> = [],
|
||||
val payload: Array<KClass<out Payload>> = [],
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
package ltd.hlaeja.controller.validation
|
||||
|
||||
import jakarta.validation.ConstraintValidator
|
||||
import jakarta.validation.ConstraintValidatorContext
|
||||
import ltd.hlaeja.form.AccountForm
|
||||
|
||||
class PasswordMatchValidator : ConstraintValidator<PasswordMatch, AccountForm> {
|
||||
override fun isValid(form: AccountForm, context: ConstraintValidatorContext): Boolean {
|
||||
val password = form.password?.toString()
|
||||
val passwordConfirm = form.passwordConfirm?.toString()
|
||||
return if (password.isNullOrEmpty() && passwordConfirm.isNullOrEmpty()) {
|
||||
true
|
||||
} else {
|
||||
password == passwordConfirm
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,16 @@ package ltd.hlaeja.dto
|
||||
@Suppress("unused")
|
||||
data class Pagination(
|
||||
val page: Int,
|
||||
val size: Int,
|
||||
val show: Int,
|
||||
val items: Int,
|
||||
val defaultSize: Int,
|
||||
) {
|
||||
val hasMore: Boolean = size == items
|
||||
val showSize: Boolean = size != defaultSize
|
||||
val hasMore: Boolean = show == items
|
||||
val showSize: Boolean = show != defaultSize
|
||||
val first: Boolean = page <= 1
|
||||
val previous: Int = page - 1
|
||||
val next: Int = page + 1
|
||||
val start: Int = (page - 1) * size + 1
|
||||
val end: Int = page * size
|
||||
val start: Int = previous * show + 1
|
||||
val end: Int = page * show
|
||||
val size: Int = previous * show + items
|
||||
}
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
package ltd.hlaeja.form
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty
|
||||
import ltd.hlaeja.controller.validation.CreateGroup
|
||||
import ltd.hlaeja.controller.validation.EditGroup
|
||||
import ltd.hlaeja.controller.validation.PasswordMatch
|
||||
|
||||
@PasswordMatch(groups = [CreateGroup::class, EditGroup::class])
|
||||
data class AccountForm(
|
||||
val username: String,
|
||||
val role: String,
|
||||
val enabled: Boolean = false,
|
||||
val password: CharSequence? = null,
|
||||
val passwordConfirm: CharSequence? = null,
|
||||
@field:NotEmpty(message = "Username cannot be empty", groups = [CreateGroup::class, EditGroup::class])
|
||||
var username: String,
|
||||
@field:NotEmpty(message = "At least one role must be selected", groups = [CreateGroup::class, EditGroup::class])
|
||||
var roles: List<String> = emptyList(),
|
||||
var enabled: Boolean = false,
|
||||
@field:NotEmpty(message = "Password cannot be empty", groups = [CreateGroup::class])
|
||||
var password: CharSequence? = null,
|
||||
@field:NotEmpty(message = "Password confirmation cannot be empty", groups = [CreateGroup::class])
|
||||
var passwordConfirm: CharSequence? = null,
|
||||
)
|
||||
|
||||
@@ -90,4 +90,11 @@ class AccountRegistryService(
|
||||
else -> Mono.error(ResponseStatusException(BAD_REQUEST, error.message))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO implement user gropes and access
|
||||
fun getRoles(): Map<String, List<String>> = mapOf(
|
||||
"Admin Group" to listOf("Admin"),
|
||||
"Operations Group" to listOf("Registry"),
|
||||
"User Group" to listOf("User"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,15 +10,19 @@ fun SpringAuthentication.toAuthenticationRequest(): Authentication.Request = Aut
|
||||
credentials as String,
|
||||
)
|
||||
|
||||
fun AccountForm.toAccountRequest(operation: (CharSequence?) -> CharSequence?): Account.Request = Account.Request(
|
||||
fun AccountForm.toAccountRequest(): Account.Request = Account.Request(
|
||||
username = username,
|
||||
password = operation(password),
|
||||
password = if (password.isNullOrEmpty()) null else password,
|
||||
enabled = enabled,
|
||||
roles = listOf("ROLE_${role.uppercase()}"),
|
||||
roles = roles.map { "ROLE_${it.uppercase()}" },
|
||||
)
|
||||
|
||||
fun Account.Response.toAccountForm(): AccountForm = AccountForm(
|
||||
username = username,
|
||||
enabled = enabled,
|
||||
role = roles.first().removePrefix("ROLE_").lowercase(),
|
||||
roles = roles.map {
|
||||
it.removePrefix("ROLE_")
|
||||
.lowercase()
|
||||
.replaceFirstChar { char -> char.uppercase() }
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user