-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #47 from KSIUJ/user-manager
Implement user management, closes #12
- Loading branch information
Showing
14 changed files
with
418 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/main/kotlin/pl/edu/uj/ii/ksi/mordor/controllers/admin/AdminController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package pl.edu.uj.ii.ksi.mordor.controllers.admin | ||
|
||
import org.springframework.security.access.annotation.Secured | ||
import org.springframework.stereotype.Controller | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.servlet.View | ||
import org.springframework.web.servlet.view.RedirectView | ||
import pl.edu.uj.ii.ksi.mordor.persistence.entities.Permission | ||
|
||
@Controller | ||
class AdminController { | ||
@Secured(Permission.ACCESS_ADMIN_PANEL_STR) | ||
@GetMapping("/admin/") | ||
fun adminPage(): View { | ||
return RedirectView("/admin/users/") | ||
} | ||
} |
122 changes: 122 additions & 0 deletions
122
src/main/kotlin/pl/edu/uj/ii/ksi/mordor/controllers/admin/UsersController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package pl.edu.uj.ii.ksi.mordor.controllers.admin | ||
|
||
import javax.validation.Valid | ||
import org.springframework.data.domain.PageRequest | ||
import org.springframework.http.HttpStatus | ||
import org.springframework.security.access.annotation.Secured | ||
import org.springframework.stereotype.Controller | ||
import org.springframework.validation.BindingResult | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.bind.annotation.ModelAttribute | ||
import org.springframework.web.bind.annotation.PathVariable | ||
import org.springframework.web.bind.annotation.PostMapping | ||
import org.springframework.web.servlet.ModelAndView | ||
import org.springframework.web.servlet.View | ||
import org.springframework.web.servlet.view.RedirectView | ||
import pl.edu.uj.ii.ksi.mordor.exceptions.BadRequestException | ||
import pl.edu.uj.ii.ksi.mordor.forms.UserForm | ||
import pl.edu.uj.ii.ksi.mordor.persistence.entities.Permission | ||
import pl.edu.uj.ii.ksi.mordor.persistence.entities.Role | ||
import pl.edu.uj.ii.ksi.mordor.persistence.entities.User | ||
import pl.edu.uj.ii.ksi.mordor.persistence.repositories.UserRepository | ||
import pl.edu.uj.ii.ksi.mordor.services.UserManagerService | ||
|
||
@Controller | ||
class UsersController(private val userRepository: UserRepository, private val userManagerService: UserManagerService) { | ||
companion object { | ||
private const val usersPerPage = 100 | ||
} | ||
|
||
@Secured(Permission.ACCESS_ADMIN_PANEL_STR) | ||
@GetMapping("/admin/users/") | ||
fun userList(): View { | ||
return RedirectView("/admin/users/0/") | ||
} | ||
|
||
@Secured(Permission.ACCESS_ADMIN_PANEL_STR) | ||
@GetMapping("/admin/users/{num}/") | ||
fun userList(@PathVariable("num") pageNumber: Int): ModelAndView { | ||
val users = userRepository.findAll(PageRequest.of(pageNumber, usersPerPage)) | ||
return ModelAndView("admin/user_list", "users", users) | ||
} | ||
|
||
@Secured(Permission.MANAGE_USERS_STR) | ||
@GetMapping("/admin/user/{id}/") | ||
fun userProfile(@PathVariable("id") userId: Long): ModelAndView { | ||
val user = userRepository.findById(userId) | ||
if (user.isPresent) { | ||
val u = user.get() | ||
val userForm = UserForm(u.id!!, u.userName, u.email, "", "", u.firstName, u.lastName, u.enabled, u.role) | ||
return ModelAndView("admin/user_profile", "user", userForm) | ||
} else { | ||
throw BadRequestException("No user for id: $userId") | ||
} | ||
} | ||
|
||
@Secured(Permission.MANAGE_USERS_STR) | ||
@PostMapping("/admin/user/{id}/") | ||
fun userProfileEdit( | ||
@PathVariable("id") userId: Long, | ||
@Valid @ModelAttribute("user") form: UserForm, | ||
result: BindingResult | ||
): ModelAndView { | ||
if (form.id != userId) { | ||
throw BadRequestException("User id mismatch") | ||
} | ||
if (form.password != form.password2) { | ||
result.rejectValue("password2", "password2.notMatch", "passwords do not match") | ||
} | ||
if (result.hasErrors()) { | ||
return ModelAndView("admin/user_profile", HttpStatus.BAD_REQUEST) | ||
} | ||
val user = userRepository.findById(form.id).orElseThrow { BadRequestException("unknown user") } | ||
user.email = form.email | ||
user.firstName = form.firstName | ||
user.lastName = form.lastName | ||
user.enabled = form.enabled | ||
user.role = form.role | ||
if (!form.password.isNullOrBlank()) { | ||
user.password = userManagerService.hashPassword(form.password) | ||
} | ||
userRepository.save(user) | ||
return ModelAndView(RedirectView("/admin/users/")) | ||
} | ||
|
||
@Secured(Permission.MANAGE_USERS_STR) | ||
@PostMapping("/admin/user/{id}/delete/") | ||
fun userProfileDelete( | ||
@PathVariable("id") userId: Long | ||
): ModelAndView { | ||
val user = userRepository.findById(userId).orElseThrow { BadRequestException("unknown user") } | ||
userRepository.delete(user) | ||
return ModelAndView(RedirectView("/admin/users/")) | ||
} | ||
|
||
@Secured(Permission.MANAGE_USERS_STR) | ||
@GetMapping("/admin/user/create/") | ||
fun userProfileCreate(): ModelAndView { | ||
return ModelAndView("admin/user_create", "user", UserForm(null, "", "", "", "", "", "", true, Role.USER)) | ||
} | ||
|
||
@Secured(Permission.MANAGE_USERS_STR) | ||
@PostMapping("/admin/user/create/") | ||
fun userProfileCreatePost(@Valid @ModelAttribute("user") form: UserForm, result: BindingResult): ModelAndView { | ||
if (!form.userName.isBlank() && userRepository.findByUserName(form.userName) != null) { | ||
result.rejectValue("userName", "username.exists", "username unavailable") | ||
} | ||
|
||
if (form.email != null && !form.email.isBlank() && userRepository.findByEmail(form.email) != null) { | ||
result.rejectValue("email", "email.exists", "email already in use") | ||
} | ||
if (form.password != form.password2) { | ||
result.rejectValue("password2", "password2.notMatch", "passwords do not match") | ||
} | ||
if (result.hasErrors()) { | ||
return ModelAndView("admin/user_create", HttpStatus.BAD_REQUEST) | ||
} | ||
val user = User(null, form.userName, form.password, form.email, | ||
form.firstName, form.lastName, form.enabled, form.role) | ||
userRepository.save(user) | ||
return ModelAndView(RedirectView("/admin/users/")) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package pl.edu.uj.ii.ksi.mordor.forms | ||
|
||
import pl.edu.uj.ii.ksi.mordor.persistence.entities.Role | ||
|
||
data class UserForm( | ||
val id: Long?, | ||
|
||
val userName: String, | ||
|
||
val email: String?, | ||
|
||
val password: String?, | ||
|
||
val password2: String?, | ||
|
||
val firstName: String?, | ||
|
||
val lastName: String?, | ||
|
||
val enabled: Boolean, | ||
|
||
val role: Role | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<!DOCTYPE html> | ||
<html lang="en" layout:decorate="~{layout}" xmlns:th="http://www.thymeleaf.org"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Mordor - Admin</title> | ||
</head> | ||
<body> | ||
<div layout:fragment="content"> | ||
<ul class="nav nav-tabs mb-1"> | ||
<li class="nav-item"> | ||
<a class="nav-link" | ||
th:classappend="${#httpServletRequest.getRequestURI().startsWith('/admin/user') ? 'active':''}" | ||
href="/admin/users/">Users</a> | ||
</li> | ||
</ul> | ||
<div layout:fragment="content-admin"> | ||
<!--CONTENT--> | ||
</div> | ||
</div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<!DOCTYPE html> | ||
<html lang="en" layout:decorate="~{admin/layout_admin}" xmlns:th="http://www.thymeleaf.org"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Create user</title> | ||
</head> | ||
<body> | ||
<div layout:fragment="content-admin"> | ||
<form th:action="@{/admin/user/create/}" th:object="${user}" method="post" style="display: inline"> | ||
<div class="form-group"> | ||
<label for="username">Username:</label> | ||
<input type="text" class="form-control" th:field="*{userName}" id="username"/> | ||
<div class="alert alert-danger" th:if="${#fields.hasErrors('userName')}" th:errors="*{email}">error</div> | ||
</div> | ||
<div class="form-group"> | ||
<label for="email">Email:</label> | ||
<input type="text" class="form-control" th:field="*{email}" id="email"/> | ||
<div class="alert alert-danger" th:if="${#fields.hasErrors('email')}" th:errors="*{email}">error</div> | ||
</div> | ||
<div class="form-group"> | ||
<label for="firstName">First Name:</label> | ||
<input type="text" class="form-control" th:field="*{firstName}" id="firstName"/> | ||
<div class="alert alert-danger" th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}">error | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<label for="lastName">Last Name:</label> | ||
<input type="text" class="form-control" th:field="*{lastName}" id="lastName"/> | ||
<div class="alert alert-danger" th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}">error</div> | ||
</div> | ||
<div class="form-group"> | ||
<label for="password">Password:</label> | ||
<input type="password" class="form-control" th:field="*{password}" id="password"/> | ||
<div class="alert alert-danger" th:if="${#fields.hasErrors('password')}" th:errors="*{password}">error</div> | ||
</div> | ||
<div class="form-group"> | ||
<label for="password2">Repeat password:</label> | ||
<input type="password" class="form-control" th:field="*{password2}" id="password2"/> | ||
<div class="alert alert-danger" th:if="${#fields.hasErrors('password2')}" th:errors="*{password2}">error | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<input type="checkbox" class="form-check-input" th:field="*{enabled}" id="enabled"> | ||
<label for="enabled" class="form-check-label">Local login enabled</label> | ||
</div> | ||
<div class="form-group"> | ||
<label for="role">Role:</label> | ||
<select class="form-control" th:field="*{role}" id="role"> | ||
<option th:each="r : ${T(pl.edu.uj.ii.ksi.mordor.persistence.entities.Role).values()}" | ||
th:value="${r}" th:text="${r.name()}"> | ||
|
||
</option> | ||
</select> | ||
</div> | ||
<button type="submit" class="btn btn-primary">Submit</button> | ||
</form> | ||
</div> | ||
</body> | ||
</html> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<!DOCTYPE html> | ||
<html lang="en" layout:decorate="~{admin/layout_admin}" xmlns:th="http://www.thymeleaf.org"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Users</title> | ||
</head> | ||
<body> | ||
<div layout:fragment="content-admin"> | ||
<table class="table"> | ||
<thead> | ||
<tr class="table-active"> | ||
<td scope="col">Id</td> | ||
<td scope="col">Username</td> | ||
<td scope="col">Email</td> | ||
<td scope="col">First name</td> | ||
<td scope="col">Last name</td> | ||
<td scope="col">Local login</td> | ||
<td scope="col">Role</td> | ||
<td scope="col"></td> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr th:each="user : ${users}"> | ||
<td scope="row" th:text="${user.id}"></td> | ||
<td th:text="${user.userName}"></td> | ||
<td th:text="${user.email}"></td> | ||
<td th:text="${user.firstName}"></td> | ||
<td th:text="${user.lastName}"></td> | ||
<td><i class="fas fa-check" th:if="${user.enabled}"></i></td> | ||
<td th:text="${user.role.name()}"></td> | ||
<td><a class="btn btn-light" href="#" th:href="@{'/admin/user/' + ${user.id} + '/'}" | ||
sec:authorize="hasRole('ROLE_MANAGE_USERS')"><i class="fas fa-edit"></i></a></td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
<div th:replace="fragments/pagination :: pagination(${users}, '/admin/users/')"> | ||
</div> | ||
<a sec:authorize="hasRole('ROLE_MANAGE_USERS')" class="btn btn-success" | ||
href="#" th:href="@{'/admin/user/create/'}">Add</a> | ||
</div> | ||
</body> | ||
</html> | ||
|
Oops, something went wrong.