Fixed CORS and auth problems
This commit is contained in:
@@ -2,6 +2,7 @@ package eu.fosil.okupamicoche.spring.conf
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.http.HttpMethod
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain
|
||||
@@ -9,6 +10,7 @@ import org.springframework.web.reactive.config.CorsRegistry
|
||||
import org.springframework.web.reactive.config.EnableWebFlux
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer
|
||||
|
||||
|
||||
@EnableWebFluxSecurity
|
||||
class SecurityConfig {
|
||||
@Bean
|
||||
@@ -17,6 +19,7 @@ class SecurityConfig {
|
||||
.and()
|
||||
.csrf().disable()
|
||||
.authorizeExchange()
|
||||
.pathMatchers(HttpMethod.OPTIONS).permitAll()
|
||||
.pathMatchers("/_matrix/**").permitAll()
|
||||
.pathMatchers("/api/public/**").permitAll()
|
||||
.pathMatchers("/api/user/**").authenticated()
|
||||
@@ -34,7 +37,7 @@ class CorsGlobalConfiguration : WebFluxConfigurer {
|
||||
override fun addCorsMappings(corsRegistry: CorsRegistry) {
|
||||
corsRegistry.addMapping("/**")
|
||||
.allowedOrigins("http://localhost:4200")
|
||||
// .allowedMethods("PUT")
|
||||
.allowedMethods("*")
|
||||
// .maxAge(3600)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,8 @@ import eu.fosil.okupamicoche.usecases.travel.*
|
||||
import mu.KotlinLogging
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.validation.annotation.Validated
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import org.springframework.web.bind.annotation.*
|
||||
import java.security.Principal
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/travel")
|
||||
@@ -28,9 +26,9 @@ class PrivateTravelRestController(
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
@RequestMapping("/create")
|
||||
suspend fun createTravel(@RequestBody @Validated travel: TravelDto): ApiResponse<Unit> {
|
||||
suspend fun createTravel(@RequestBody @Validated travel: TravelDto, principal: Principal): ApiResponse<Unit> {
|
||||
return response {
|
||||
val driver = userRepository.findByIdOrNull(authService.currentUser().id)
|
||||
val driver = userRepository.findByIdOrNull(authService.currentUser(principal).id)
|
||||
?: throw UserIdNotFoundException("Current user not found.")
|
||||
if (travel.id != null && (travelRepository.findByIdOrNull(travel.id) != null))
|
||||
throw CannotDuplicateIdException("Travel id already exists.")
|
||||
@@ -42,33 +40,34 @@ class PrivateTravelRestController(
|
||||
}
|
||||
|
||||
@RequestMapping("/cancel")
|
||||
suspend fun cancelTravel(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
|
||||
suspend fun cancelTravel(@RequestParam @Validated travelId: TravelId, principal: Principal): ApiResponse<Unit> {
|
||||
return response {
|
||||
throwErrorIfCannotEditTravel(travelId)
|
||||
throwErrorIfCannotEditTravel(travelId, principal)
|
||||
CancelTravel(travelRepository).cancelTravel(travelId)
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/delete")
|
||||
suspend fun deleteTravel(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
|
||||
suspend fun deleteTravel(@RequestParam @Validated travelId: TravelId, principal: Principal): ApiResponse<Unit> {
|
||||
return response {
|
||||
throwErrorIfCannotEditTravel(travelId)
|
||||
throwErrorIfCannotEditTravel(travelId, principal)
|
||||
DeleteTravel(travelRepository).deleteTravel(travelId)
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/edit")
|
||||
suspend fun editTravel(@RequestBody @Validated travel: TravelDto): ApiResponse<Unit> {
|
||||
suspend fun editTravel(@RequestBody @Validated travel: TravelDto, principal: Principal): ApiResponse<Unit> {
|
||||
return response {
|
||||
throwErrorIfCannotEditTravel(travel.id)
|
||||
throwErrorIfCannotEditTravel(travel.id, principal)
|
||||
EditTravel(travelRepository).editTravel(travel.toTravel(userRepository))
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/listusertravels")
|
||||
suspend fun listUserTravels(): ApiResponse<ListDto<TravelDto>> {
|
||||
suspend fun listUserTravels(principal: Principal): ApiResponse<ListDto<TravelDto>> {
|
||||
logger.debug { "principal=$principal" }
|
||||
return response {
|
||||
val userId = authService.currentUser().id
|
||||
val userId = authService.currentUser(principal).id
|
||||
val useCase = ListUserTravels(travelRepository)
|
||||
val travels = useCase.listUserTravels(userId).map { t -> TravelDto(t) }
|
||||
logger.debug { "travels=$travels" }
|
||||
@@ -77,17 +76,17 @@ class PrivateTravelRestController(
|
||||
}
|
||||
|
||||
@RequestMapping("/join")
|
||||
suspend fun join(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
|
||||
suspend fun join(@RequestParam @Validated travelId: TravelId, principal: Principal): ApiResponse<Unit> {
|
||||
return response {
|
||||
val userId = authService.currentUser().id
|
||||
val userId = authService.currentUser(principal).id
|
||||
AddTraveler(userRepository, travelRepository).addTraveler(travelId, userId)
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/leave")
|
||||
suspend fun leave(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
|
||||
suspend fun leave(@RequestParam @Validated travelId: TravelId, principal: Principal): ApiResponse<Unit> {
|
||||
return response {
|
||||
val userId = authService.currentUser().id
|
||||
val userId = authService.currentUser(principal).id
|
||||
RemoveTraveler(userRepository, travelRepository).removeTraveler(travelId, userId)
|
||||
}
|
||||
}
|
||||
@@ -95,10 +94,11 @@ class PrivateTravelRestController(
|
||||
@RequestMapping("/addtraveler")
|
||||
suspend fun addTraveler(
|
||||
@RequestParam @Validated travelId: TravelId,
|
||||
@RequestParam @Validated userId: UserId
|
||||
@RequestParam @Validated userId: UserId,
|
||||
principal: Principal
|
||||
): ApiResponse<Unit> {
|
||||
return response {
|
||||
throwErrorIfCannotEditTravel(travelId)
|
||||
throwErrorIfCannotEditTravel(travelId,principal)
|
||||
AddTraveler(userRepository, travelRepository).addTraveler(travelId, userId)
|
||||
}
|
||||
}
|
||||
@@ -106,19 +106,21 @@ class PrivateTravelRestController(
|
||||
@RequestMapping("/removetraveler")
|
||||
suspend fun removeTraveler(
|
||||
@RequestParam @Validated travelId: TravelId,
|
||||
@RequestParam @Validated userId: UserId
|
||||
@RequestParam @Validated userId: UserId,
|
||||
principal: Principal
|
||||
): ApiResponse<Unit> {
|
||||
return response {
|
||||
throwErrorIfCannotEditTravel(travelId)
|
||||
throwErrorIfCannotEditTravel(travelId, principal)
|
||||
RemoveTraveler(userRepository, travelRepository).removeTraveler(travelId, userId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun throwErrorIfCannotEditTravel(
|
||||
travelId: TravelId?,
|
||||
principal: Principal,
|
||||
message: String = "Only admins and travel driver can modify this travel."
|
||||
) {
|
||||
if (!authService.canEditTravel(travelId))
|
||||
if (!authService.canEditTravel(travelId, principal))
|
||||
throw InsufficientPermissions(message)
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import org.springframework.validation.annotation.Validated
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import java.security.Principal
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/user")
|
||||
@@ -22,9 +23,9 @@ class PrivateUserRestController(
|
||||
) : ApiRestController {
|
||||
|
||||
@RequestMapping("/user")
|
||||
suspend fun getCurrentUserCreateIfNeeded(): ApiResponse<UserDto> {
|
||||
suspend fun getCurrentUserCreateIfNeeded(principal: Principal): ApiResponse<UserDto> {
|
||||
return response {
|
||||
val userKeycloak = authService.currentUser()
|
||||
val userKeycloak = authService.currentUser(principal)
|
||||
var user = userRepository.findByIdOrNull(userKeycloak.id)
|
||||
|
||||
if (user == null) {
|
||||
@@ -37,9 +38,12 @@ class PrivateUserRestController(
|
||||
}
|
||||
|
||||
@RequestMapping("/create")
|
||||
suspend fun createUser(@RequestBody @Validated createUserDto: CreateUserDto): ApiResponse<Unit> {
|
||||
suspend fun createUser(
|
||||
@RequestBody @Validated createUserDto: CreateUserDto,
|
||||
principal: Principal
|
||||
): ApiResponse<Unit> {
|
||||
return response {
|
||||
if (!authService.isAdmin())
|
||||
if (!authService.isAdmin(principal))
|
||||
throw InsufficientPermissions("Only admins can create users.")
|
||||
CreateUser(userRepository).createUser(createUserDto.toUser())
|
||||
}
|
||||
@@ -53,17 +57,17 @@ class PrivateUserRestController(
|
||||
}
|
||||
|
||||
@RequestMapping("/delete")
|
||||
suspend fun deleteUser(@RequestBody @Validated userId: UserId): ApiResponse<Unit> {
|
||||
suspend fun deleteUser(@RequestBody @Validated userId: UserId, principal: Principal): ApiResponse<Unit> {
|
||||
return response {
|
||||
throwErrorIfCannotEditUser(userId)
|
||||
throwErrorIfCannotEditUser(userId, principal)
|
||||
DeleteUser(userRepository).deleteUser(userId)
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/edit")
|
||||
suspend fun editUser(@RequestBody @Validated userDto: UserDto): ApiResponse<Unit> {
|
||||
suspend fun editUser(@RequestBody @Validated userDto: UserDto, principal: Principal): ApiResponse<Unit> {
|
||||
return response {
|
||||
throwErrorIfCannotEditUser(userDto.id)
|
||||
throwErrorIfCannotEditUser(userDto.id, principal)
|
||||
EditUser(userRepository).editUser(userDto.toUser(userRepository))
|
||||
}
|
||||
}
|
||||
@@ -77,9 +81,10 @@ class PrivateUserRestController(
|
||||
|
||||
private fun throwErrorIfCannotEditUser(
|
||||
userId: UserId?,
|
||||
principal: Principal,
|
||||
message: String = "Only admins and travel driver can modify this user."
|
||||
) {
|
||||
if (!authService.canEditUser(userId))
|
||||
if (!authService.canEditUser(userId, principal))
|
||||
throw InsufficientPermissions(message)
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,40 @@
|
||||
package eu.fosil.okupamicoche.spring.services
|
||||
|
||||
import eu.fosil.okupamicoche.entities.*
|
||||
import eu.fosil.okupamicoche.entities.TravelId
|
||||
import eu.fosil.okupamicoche.entities.UserId
|
||||
import eu.fosil.okupamicoche.entities.UserIdNotFoundException
|
||||
import eu.fosil.okupamicoche.entities.UserKeycloak
|
||||
import eu.fosil.okupamicoche.repositories.TravelRepository
|
||||
import eu.fosil.okupamicoche.repositories.UserRepository
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.oauth2.jwt.Jwt
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken
|
||||
import org.springframework.stereotype.Service
|
||||
import java.security.Principal
|
||||
|
||||
@Service
|
||||
class AuthService(
|
||||
private val userRepository: UserRepository,
|
||||
private val travelRepository: TravelRepository
|
||||
) {
|
||||
/**
|
||||
* Devuelve el id del usuario actual.
|
||||
*/
|
||||
fun currentUser(): UserKeycloak {
|
||||
val authentication = SecurityContextHolder.getContext().authentication
|
||||
if (authentication.principal is Jwt) {
|
||||
val jwt = authentication.principal as Jwt
|
||||
return UserKeycloak(jwt.claims)
|
||||
fun currentUser(principal: Principal): UserKeycloak {
|
||||
if (principal is JwtAuthenticationToken) {
|
||||
return UserKeycloak(principal.token.claims)
|
||||
}
|
||||
throw UserIdNotFoundException()
|
||||
}
|
||||
|
||||
fun isAdmin(): Boolean {
|
||||
return currentUser().admin
|
||||
fun isAdmin(principal: Principal): Boolean {
|
||||
return currentUser(principal).admin
|
||||
}
|
||||
|
||||
fun canEditTravel(travelId: TravelId?): Boolean {
|
||||
fun canEditTravel(travelId: TravelId?, principal: Principal): Boolean {
|
||||
val travel = travelRepository.findByIdOrNull(travelId) ?: return false
|
||||
return currentUser().admin || currentUser().id == travel.driver.id
|
||||
return currentUser(principal).admin || currentUser(principal).id == travel.driver.id
|
||||
}
|
||||
|
||||
fun canEditUser(userId: UserId?): Boolean {
|
||||
fun canEditUser(userId: UserId?, principal: Principal): Boolean {
|
||||
if (userId == null) return false
|
||||
return currentUser().admin || currentUser().id == userId
|
||||
return currentUser(principal).admin || currentUser(principal).id == userId
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user