Changed logins to have proper redirections
This commit is contained in:
parent
1bce01e07e
commit
20b9dcf1d5
@ -3,6 +3,7 @@ package uk.co.neviyn.booru
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.data.domain.Page
|
||||
import org.springframework.data.domain.PageRequest
|
||||
import org.springframework.security.access.annotation.Secured
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||
import org.springframework.stereotype.Controller
|
||||
import org.springframework.ui.Model
|
||||
@ -33,38 +34,68 @@ class BaseController
|
||||
}
|
||||
|
||||
@GetMapping("/login")
|
||||
fun login(model: Model, error: String?, logout: String?): String? {
|
||||
fun login(model: Model, error: String?, logout: String?, @RequestParam redirect: String): String? {
|
||||
if (error != null) model.addAttribute("error", "Your username and password is invalid.")
|
||||
if (logout != null) model.addAttribute("message", "You have been logged out successfully.")
|
||||
model.addAttribute("redirect", redirect)
|
||||
return "login"
|
||||
}
|
||||
}
|
||||
|
||||
fun userCanEdit(userDetails: CustomUserDetails?, image: Image): Boolean =
|
||||
userDetails != null && (userDetails.authorities.any { it.authority == "ADMIN" } || userDetails.getId() == image.uploader.id)
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/view/{id}")
|
||||
class SingleImageViewController
|
||||
@Autowired constructor(
|
||||
val imageRepository: ImageRepository,
|
||||
val tagRepository: TagRepository
|
||||
){
|
||||
) {
|
||||
@GetMapping
|
||||
fun viewSingleImage(@PathVariable id: Long, model: Model, @AuthenticationPrincipal userDetails: CustomUserDetails?) : String {
|
||||
fun viewSingleImage(@PathVariable id: Long, model: Model, @AuthenticationPrincipal userDetails: CustomUserDetails?): String {
|
||||
val image = imageRepository.findById(id).get()
|
||||
val tagData = image.tags.sortedBy { it.tag }
|
||||
model.addAttribute("image", image)
|
||||
model.addAttribute("tags", tagData)
|
||||
model.addAttribute("title", tagData.joinToString { it.tag })
|
||||
model.addAttribute("isUploader", (userDetails != null && (userDetails.authorities.any { it.authority == "ADMIN" } || userDetails.getId() == image.uploader.id)))
|
||||
model.addAttribute("isUploader", userCanEdit(userDetails, image))
|
||||
return "single"
|
||||
}
|
||||
|
||||
@PostMapping("/removetag")
|
||||
@PostMapping("/addtag")
|
||||
@Secured
|
||||
@Transactional
|
||||
fun removeTagFromImage(@PathVariable id: Long, @RequestParam("tagId") tagID: Long, model: Model, @AuthenticationPrincipal userDetails: CustomUserDetails?) : String {
|
||||
fun addTagToImage(
|
||||
@PathVariable id: Long,
|
||||
@RequestParam("tagName") tagName: String,
|
||||
model: Model,
|
||||
@AuthenticationPrincipal userDetails: CustomUserDetails?
|
||||
): String {
|
||||
val image = imageRepository.findById(id).get()
|
||||
if(userDetails == null || userDetails.getId() != image.uploader.id || !userDetails.authorities.any { it.authority == "ADMIN" }) return "redirect:/view/$id"
|
||||
if (!userCanEdit(userDetails, image)) return "redirect:/view/$id"
|
||||
var newTag = tagRepository.findByTagIs(tagName) ?: Tag(tag = tagName)
|
||||
if (image.tags.contains(newTag)) return "redirect:/view/$id" // Don't add a tag that an image already has
|
||||
newTag.amount += 1
|
||||
newTag = tagRepository.save(newTag)
|
||||
image.tags.add(newTag)
|
||||
imageRepository.save(image)
|
||||
return "redirect:/view/$id"
|
||||
}
|
||||
|
||||
@PostMapping("/removetag")
|
||||
@Secured
|
||||
@Transactional
|
||||
fun removeTagFromImage(
|
||||
@PathVariable id: Long,
|
||||
@RequestParam("tagId") tagID: Long,
|
||||
model: Model,
|
||||
@AuthenticationPrincipal userDetails: CustomUserDetails?
|
||||
): String {
|
||||
val image = imageRepository.findById(id).get()
|
||||
if (!userCanEdit(userDetails, image)) return "redirect:/view/$id"
|
||||
val targetTag = image.tags.find { it.id == tagID }
|
||||
if(targetTag != null){
|
||||
if (targetTag != null) {
|
||||
targetTag.amount -= 1
|
||||
tagRepository.save(targetTag)
|
||||
image.tags.remove(targetTag)
|
||||
@ -127,13 +158,15 @@ class MemberController
|
||||
) {
|
||||
|
||||
@GetMapping
|
||||
fun memberDetails(@AuthenticationPrincipal userDetails: CustomUserDetails, model: Model) : String {
|
||||
fun memberDetails(@AuthenticationPrincipal userDetails: CustomUserDetails?, model: Model): String {
|
||||
if (userDetails == null) return "redirect:/login?redirect=/user"
|
||||
val user = DisplayUser(userDetails.getId(), userDetails.username, userDetails.getEmail(), "", "")
|
||||
model.addAttribute("userData", user)
|
||||
return "user"
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Secured
|
||||
fun updateLoggedInUser(@Valid @ModelAttribute userData: DisplayUser, @AuthenticationPrincipal userDetails: CustomUserDetails, model: Model): String {
|
||||
if (userData.id == userDetails.getId() && passwordEncoder().matches(userData.oldPassword, userDetails.password)) {
|
||||
val user = memberRepository.findById(userDetails.getId()).get()
|
||||
@ -162,9 +195,13 @@ class UploadController
|
||||
) {
|
||||
|
||||
@GetMapping
|
||||
fun showUploadPage(): String = "upload"
|
||||
fun showUploadPage(@AuthenticationPrincipal userDetails: CustomUserDetails?): String {
|
||||
if (userDetails == null) return "redirect:/login?redirect=/upload"
|
||||
return "upload"
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Secured
|
||||
fun uploadFile(
|
||||
@AuthenticationPrincipal userDetails: CustomUserDetails,
|
||||
@RequestParam file: MultipartFile,
|
||||
@ -183,12 +220,13 @@ class UploadController
|
||||
}
|
||||
|
||||
@DeleteMapping("/d/{imageID}")
|
||||
@Secured
|
||||
@Transactional
|
||||
fun deleteUpload(@PathVariable imageID: Long, @AuthenticationPrincipal userDetails: CustomUserDetails) : String {
|
||||
fun deleteUpload(@PathVariable imageID: Long, @AuthenticationPrincipal userDetails: CustomUserDetails): String {
|
||||
val target = imageRepository.findById(imageID)
|
||||
if(target.isEmpty) return "gallery" // No image with ID
|
||||
if (target.isEmpty) return "gallery" // No image with ID
|
||||
val rTarget = target.get()
|
||||
return if(userDetails.authorities.any { it.authority == "ADMIN" } || rTarget.uploader.id == userDetails.getId()) {
|
||||
return if (userDetails.authorities.any { it.authority == "ADMIN" } || rTarget.uploader.id == userDetails.getId()) {
|
||||
rTarget.tags.map { it.amount = it.amount - 1 }
|
||||
tagRepository.saveAll(rTarget.tags)
|
||||
imageRepository.deleteTagAssociations(rTarget.id)
|
||||
|
@ -13,7 +13,12 @@ import org.springframework.security.core.userdetails.UserDetailsService
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
||||
import org.springframework.security.crypto.password.PasswordEncoder
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler
|
||||
import org.springframework.stereotype.Service
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
|
||||
@Configuration
|
||||
class SecurityConfig
|
||||
@ -21,9 +26,9 @@ class SecurityConfig
|
||||
val userDetailsService: CustomUserDetailsService
|
||||
) : WebSecurityConfigurerAdapter() {
|
||||
override fun configure(http: HttpSecurity) {
|
||||
http.authorizeRequests().antMatchers("/upload/**", "/user/**").hasAuthority("USER")
|
||||
http.authorizeRequests()
|
||||
.anyRequest().permitAll().and()
|
||||
.formLogin().loginPage("/login").permitAll().and()
|
||||
.formLogin().successHandler(RefererRedirectionAuthenticationSuccessHandler()).loginPage("/login").permitAll().and()
|
||||
.logout().logoutSuccessUrl("/").permitAll().and()
|
||||
.httpBasic()
|
||||
}
|
||||
@ -66,3 +71,11 @@ class CustomUserDetailsService
|
||||
|
||||
@Bean
|
||||
fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder()
|
||||
|
||||
// Keeps us on the same page when logging in
|
||||
class RefererRedirectionAuthenticationSuccessHandler : SimpleUrlAuthenticationSuccessHandler(), AuthenticationSuccessHandler {
|
||||
|
||||
override fun determineTargetUrl(request: HttpServletRequest?, response: HttpServletResponse?): String {
|
||||
return request!!.getParameter("redirect")
|
||||
}
|
||||
}
|
@ -1,58 +1,73 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
|
||||
<html lang="en" xmlns:sec="http://www.thymeleaf.org/extras/spring-security" xmlns:th="http://www.thymeleaf.org">
|
||||
<body>
|
||||
<div th:fragment="header" th:remove="tag">
|
||||
<meta charset="UTF-8">
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/svg+xml" th:href="@{/webjars/bootstrap-icons/1.4.1/icons/images.svg}">
|
||||
<!-- Common styles and scripts -->
|
||||
<link th:href="@{/webjars/bootstrap/5.0.0-beta3/css/bootstrap.min.css}" rel="stylesheet">
|
||||
<script th:src="@{/webjars/bootstrap/5.0.0-beta3/js/bootstrap.bundle.min.js}"></script>
|
||||
<link th:href="@{/webjars/bootstrap-icons/1.4.1/font/bootstrap-icons.css}" rel="stylesheet">
|
||||
<meta charset="UTF-8">
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" th:href="@{/webjars/bootstrap-icons/1.4.1/icons/images.svg}" type="image/svg+xml">
|
||||
<!-- Common styles and scripts -->
|
||||
<link rel="stylesheet" th:href="@{/webjars/bootstrap/5.0.0-beta3/css/bootstrap.min.css}">
|
||||
<script th:src="@{/webjars/bootstrap/5.0.0-beta3/js/bootstrap.bundle.min.js}"></script>
|
||||
<link rel="stylesheet" th:href="@{/webjars/bootstrap-icons/1.4.1/font/bootstrap-icons.css}">
|
||||
</div>
|
||||
<div th:fragment="navbar">
|
||||
<form id="logoutForm" method="POST" th:action="@{/logout}">
|
||||
<input name="${_csrf.parameterName}" type="hidden" value="${_csrf.token}"/>
|
||||
</form>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
Booru
|
||||
</a>
|
||||
<button aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation" class="navbar-toggler"
|
||||
data-bs-target="#navbarNavAltMarkup" data-bs-toggle="collapse" type="button">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
|
||||
<div class="navbar-nav me-auto">
|
||||
<a class="nav-link" th:href="${#mvc.url('IC#getGalleryPage').build()}">Posts</a>
|
||||
<a class="nav-link" th:href="${#mvc.url('UC#showUploadPage').build()}">Upload</a>
|
||||
<a class="nav-link" th:href="${#mvc.url('MC#memberDetails').build()}">My Account</a>
|
||||
<form id="logoutForm" method="POST" th:action="@{/logout}">
|
||||
<input name="${_csrf.parameterName}" type="hidden" value="${_csrf.token}"/>
|
||||
</form>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
Booru
|
||||
</a>
|
||||
<button aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation" class="navbar-toggler"
|
||||
data-bs-target="#navbarNavAltMarkup" data-bs-toggle="collapse" type="button">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
|
||||
<div class="navbar-nav me-auto">
|
||||
<a class="nav-link" th:href="${#mvc.url('IC#getGalleryPage').build()}">Posts</a>
|
||||
<a class="nav-link" th:href="${#mvc.url('UC#showUploadPage').build()}">Upload</a>
|
||||
<a class="nav-link" th:href="${#mvc.url('MC#memberDetails').build()}">My Account</a>
|
||||
</div>
|
||||
<div class="d-flex" sec:authorize="isAuthenticated()">
|
||||
<span class="navbar-text" sec:authentication="name"></span>
|
||||
<a class="nav-link" onclick="document.forms['logoutForm'].submit()" style="cursor: pointer"><i
|
||||
class="bi bi-box-arrow-right me-1"></i>Logout</a>
|
||||
</div>
|
||||
<form class="form-sign_in" method="POST" sec:authorize="!isAuthenticated()" th:action="@{/login}">
|
||||
<div class="form-group d-flex flex-row align-items-center">
|
||||
<input id="redirectURL" name="redirect" type="hidden" value="">
|
||||
<!--suppress HtmlFormInputWithoutLabel -->
|
||||
<input class="form-control form-control-sm" id="username" name="username" placeholder="Username" type="text">
|
||||
<!--suppress HtmlFormInputWithoutLabel -->
|
||||
<input class="form-control form-control-sm" id="password" name="password" placeholder="Password" type="password">
|
||||
<input name="${_csrf.parameterName}" type="hidden" value="${_csrf.token}"/>
|
||||
<button class="btn btn-primary btn-sm text-nowrap" type="submit">Log In</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div sec:authorize="isAuthenticated()" class="d-flex">
|
||||
<span sec:authentication="name" class="navbar-text"></span>
|
||||
<a class="nav-link" onclick="document.forms['logoutForm'].submit()" style="cursor: pointer"><i
|
||||
class="bi bi-box-arrow-right me-1"></i>Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<script>
|
||||
// Set logging in via this page to redirect back to this exact page
|
||||
document.getElementById('redirectURL').setAttribute('value', window.location.href)
|
||||
</script>
|
||||
</nav>
|
||||
</div>
|
||||
<div th:fragment="searchBarRow">
|
||||
<form action="/gallery" class="text-center row row-cols-auto justify-content-center align-items-center mt-2">
|
||||
<div class="col-10 col-lg-6">
|
||||
<!--suppress HtmlFormInputWithoutLabel -->
|
||||
<input class="form-control" id="imageSearch"
|
||||
name="tags"
|
||||
pattern="[a-zA-Z0-9\s_]*"
|
||||
placeholder="Ex: blue_eyes smile"
|
||||
th:value="${#request.getParameter('tags')}"
|
||||
type="search">
|
||||
</div>
|
||||
<div class="col-2 col-lg-1 d-grid">
|
||||
<button class="btn btn-primary" type="submit">Search</button>
|
||||
</div>
|
||||
</form>
|
||||
<form action="/gallery" class="text-center row row-cols-auto justify-content-center align-items-center mt-2">
|
||||
<div class="col-10 col-lg-6">
|
||||
<!--suppress HtmlFormInputWithoutLabel -->
|
||||
<input class="form-control" id="imageSearch"
|
||||
name="tags"
|
||||
pattern="[a-zA-Z0-9\s_]*"
|
||||
placeholder="Ex: blue_eyes smile"
|
||||
th:value="${#request.getParameter('tags')}"
|
||||
type="search">
|
||||
</div>
|
||||
<div class="col-2 col-lg-1 d-grid">
|
||||
<button class="btn btn-primary" type="submit">Search</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -16,6 +16,7 @@
|
||||
<div class="col-8 text-center">
|
||||
<form class="form-sign_in" method="POST" th:action="@{/login}">
|
||||
<div class="form-group mt-3">
|
||||
<input name="redirect" th:value="${redirect}" type="hidden">
|
||||
<span th:text="${message}"></span>
|
||||
<div class="form-floating mb-3">
|
||||
<input autofocus class="form-control form-control-lg" id="username" name="username" type="text">
|
||||
@ -39,7 +40,7 @@
|
||||
</div>
|
||||
<div class="row justify-content-center mt-3">
|
||||
<div class="col text-center">
|
||||
<a class="btn btn-secondary btn-lg" th:href="${#mvc.url('BC#landingPage').build()}" role="button">Home</a>
|
||||
<a class="btn btn-secondary btn-lg" role="button" th:href="${#mvc.url('BC#landingPage').build()}">Home</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -15,13 +15,18 @@
|
||||
<a class="text-decoration-none"
|
||||
th:href="${#mvc.url('IC#getGalleryPage').arg(1, tag.tag).build()}"
|
||||
th:text="${tag.tag} + ' '">tag</a><span th:text="${tag.amount}"></span>
|
||||
<form th:action="${'/view/' + image.id + '/removetag'}" method="post" th:if="${isUploader}">
|
||||
<input type="hidden" th:value="${tag.id}" name="tagId">
|
||||
<form method="post" th:action="${'/view/' + image.id + '/removetag'}" th:if="${isUploader}">
|
||||
<input name="tagId" th:value="${tag.id}" type="hidden">
|
||||
<button type="submit">-</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Uploaded by: <span th:text="${image.uploader.name}"></span></p>
|
||||
<form method="post" th:action="'/view/' + ${image.id} + '/addtag'" th:if="${isUploader}">
|
||||
<!--suppress HtmlFormInputWithoutLabel -->
|
||||
<input name="tagName" placeholder="Add a new tag" type="text">
|
||||
<button type="submit">+</button>
|
||||
</form>
|
||||
<form th:action="'/upload/d/' + ${image.id}"
|
||||
th:if="${isUploader}"
|
||||
th:method="delete">
|
||||
|
Loading…
Reference in New Issue
Block a user