Fixed CORS and auth problems

This commit is contained in:
2021-03-29 17:03:10 +02:00
parent 2549d91720
commit 6742ffb959
4 changed files with 58 additions and 49 deletions

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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
}
}