Fixes in edit/create travel

This commit is contained in:
Eneko Nieto
2021-02-09 13:29:28 +01:00
parent 540bece552
commit 97964f2659
10 changed files with 112 additions and 54 deletions

View File

@@ -1,22 +1,24 @@
package eu.fosil.okupamicoche.dto
import eu.fosil.okupamicoche.entities.User
import eu.fosil.okupamicoche.repositories.UserRepository
class CreateUserDto(
private val id: String,
private val username: String,
private val name: String,
private val email: String?
) {
constructor(user: User) : this(
user.id,
user.username,
user.name,
user.email
)
fun toUser(userRepository: UserRepository): User {
fun toUser(): User {
return User(
id = id,
username = username,
name = name,
email = email
)

View File

@@ -7,6 +7,7 @@ import eu.fosil.okupamicoche.repositories.UserRepository
class UserDto(
// Los campos deben ser públicos para que aparezcan en el JSON
val id: UserId,
val username: String,
val matrixId: String?,
val name: String,
val email: String?,
@@ -15,6 +16,7 @@ class UserDto(
) {
constructor(user: User) : this(
user.id,
user.username,
user.matrixId,
user.name,
user.email,
@@ -25,6 +27,7 @@ class UserDto(
fun toUser(userRepository: UserRepository): User {
return User(
id,
username,
matrixId,
name,
email,

View File

@@ -2,16 +2,17 @@ package eu.fosil.okupamicoche.dto
import eu.fosil.okupamicoche.entities.User
import eu.fosil.okupamicoche.entities.UserId
import eu.fosil.okupamicoche.repositories.UserRepository
class UserInfoDto(
// Los campos deben ser públicos para que aparezcan en el JSON
val id: UserId,
val username: String,
val matrixId: String?,
val name: String
) {
constructor(user: User) : this(
user.id,
user.username,
user.matrixId,
user.name
)
@@ -19,6 +20,7 @@ class UserInfoDto(
fun toUser(): User {
return User(
id,
username,
matrixId,
name
)

View File

@@ -1,5 +1,9 @@
package eu.fosil.okupamicoche.entities
class UserIdNotFoundException : Exception()
class UserIdNotFoundException(msg: String = "") : Exception(msg)
class UserNotSpecifiedException : Exception()
class InsufficientPermissions(msg: String = "") : Exception(msg)
class UserNotSpecifiedException(msg: String = "") : Exception(msg)
class CannotDuplicateIdException(msg: String = "") : Exception(msg)

View File

@@ -7,6 +7,7 @@ typealias UserId = String
@Entity
class User(
@Id var id: UserId,
var username: String,
var matrixId: String? = null,
var name: String,
var email: String? = null,

View File

@@ -0,0 +1,31 @@
package eu.fosil.okupamicoche.entities
class UserKeycloak(
val id: UserId,
val username: String,
val admin: Boolean,
val name: String,
val email: String
) {
constructor(claims: Map<String, Any>) : this(
claims["sub"].toString(),
claims["preferred_username"].toString(),
try {
claims["admin"] as Boolean
} catch (e: Exception) {
false
},
claims["name"].toString(),
claims["email"].toString()
)
fun toUser(): User {
return User(
id,
username,
"@$username:fosil.eu",
name,
email
)
}
}

View File

@@ -2,9 +2,6 @@ package eu.fosil.okupamicoche.spring.controller
import eu.fosil.okupamicoche.entities.ApiErrorResponse
import eu.fosil.okupamicoche.entities.ApiResponse
import eu.fosil.okupamicoche.entities.UserIdNotFoundException
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.oauth2.jwt.Jwt
interface ApiRestController {
fun <T> response(function: () -> T): ApiResponse<T> {
@@ -16,28 +13,4 @@ interface ApiRestController {
ApiErrorResponse(e.javaClass.canonicalName, e.message)
}
}
/**
* Devuelve el id del usuario actual.
*/
fun getCurrentUserId(): String {
val authentication = SecurityContextHolder.getContext().authentication
if (authentication.principal is Jwt) {
val jwt = authentication.principal as Jwt
return jwt.claims["sub"].toString()
}
throw UserIdNotFoundException()
}
/**
* Devuelve el id del usuario actual.
*/
fun getCurrentUserClaims(): Map<String, Any> {
val authentication = SecurityContextHolder.getContext().authentication
if (authentication.principal is Jwt) {
val jwt = authentication.principal as Jwt
return jwt.claims
}
throw UserIdNotFoundException()
}
}

View File

@@ -3,12 +3,10 @@ package eu.fosil.okupamicoche.spring.controller
import eu.fosil.okupamicoche.dto.ListDto
import eu.fosil.okupamicoche.dto.TravelDto
import eu.fosil.okupamicoche.dto.UserInfoDto
import eu.fosil.okupamicoche.entities.ApiResponse
import eu.fosil.okupamicoche.entities.TravelId
import eu.fosil.okupamicoche.entities.UserId
import eu.fosil.okupamicoche.entities.UserIdNotFoundException
import eu.fosil.okupamicoche.entities.*
import eu.fosil.okupamicoche.repositories.TravelRepository
import eu.fosil.okupamicoche.repositories.UserRepository
import eu.fosil.okupamicoche.spring.services.AuthService
import eu.fosil.okupamicoche.usecases.travel.*
import org.springframework.data.repository.findByIdOrNull
import org.springframework.validation.annotation.Validated
@@ -17,6 +15,7 @@ import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/api/travel")
class PrivateTravelRestController(
private val authService: AuthService,
private val userRepository: UserRepository,
private val travelRepository: TravelRepository
) : ApiRestController {
@@ -24,7 +23,10 @@ class PrivateTravelRestController(
@RequestMapping("/create")
fun createTravel(@RequestBody @Validated travel: TravelDto): ApiResponse<Unit> {
return response {
val driver = userRepository.findByIdOrNull(getCurrentUserId()) ?: throw UserIdNotFoundException()
val driver = userRepository.findByIdOrNull(authService.currentUser().id)
?: throw UserIdNotFoundException("Current user not found.")
if (travelRepository.findByIdOrNull(travel.id) != null)
throw CannotDuplicateIdException("Travel id already exists.")
println("travel des=${travel.description}")
travel.driverInfo = UserInfoDto(driver)
CreateTravel(travelRepository).createTravel(travel.toTravel(userRepository))
@@ -41,13 +43,17 @@ class PrivateTravelRestController(
@RequestMapping("/delete")
fun deleteTravel(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
return response {
if (!authService.canEditTravel(travelId))
throw InsufficientPermissions("Only admins and travel driver can delete this travel.")
DeleteTravel(travelRepository).deleteTravel(travelId)
}
}
@RequestMapping("/edit")
fun editTravel(@RequestParam @Validated travel: TravelDto): ApiResponse<Unit> {
fun editTravel(@RequestBody @Validated travel: TravelDto): ApiResponse<Unit> {
return response {
if (!authService.canEditTravel(travel.id))
throw InsufficientPermissions("Only admins and travel driver can edit this travel.")
EditTravel(travelRepository).editTravel(travel.toTravel(userRepository))
}
}
@@ -55,7 +61,7 @@ class PrivateTravelRestController(
@RequestMapping("/listusertravels")
fun listUserTravels(): ApiResponse<ListDto<TravelDto>> {
return response {
val userId = getCurrentUserId()
val userId = authService.currentUser().id
val useCase = ListUserTravels(travelRepository)
val travels = useCase.listUserTravels(userId).map { t -> TravelDto(t) }
println("travels=$travels")
@@ -66,7 +72,7 @@ class PrivateTravelRestController(
@RequestMapping("/join")
fun join(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
return response {
val userId = getCurrentUserId()
val userId = authService.currentUser().id
AddTraveler(userRepository, travelRepository).addTraveler(travelId, userId)
}
}
@@ -74,7 +80,7 @@ class PrivateTravelRestController(
@RequestMapping("/leave")
fun leave(@RequestParam @Validated travelId: TravelId): ApiResponse<Unit> {
return response {
val userId = getCurrentUserId()
val userId = authService.currentUser().id
RemoveTraveler(userRepository, travelRepository).removeTraveler(travelId, userId)
}
}

View File

@@ -3,34 +3,32 @@ package eu.fosil.okupamicoche.spring.controller
import eu.fosil.okupamicoche.dto.CreateUserDto
import eu.fosil.okupamicoche.dto.UserDto
import eu.fosil.okupamicoche.entities.ApiResponse
import eu.fosil.okupamicoche.entities.User
import eu.fosil.okupamicoche.entities.InsufficientPermissions
import eu.fosil.okupamicoche.entities.UserId
import eu.fosil.okupamicoche.repositories.UserRepository
import eu.fosil.okupamicoche.spring.services.AuthService
import eu.fosil.okupamicoche.usecases.user.*
import org.springframework.data.repository.findByIdOrNull
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.CrossOrigin
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api/user")
class PrivateUserRestController(private val userRepository: UserRepository) : ApiRestController {
class PrivateUserRestController(
private val authService: AuthService,
private val userRepository: UserRepository
) : ApiRestController {
@RequestMapping("/user")
fun getCurrentUserCreateIfNeeded(): ApiResponse<UserDto> {
return response {
var user = userRepository.findByIdOrNull(getCurrentUserId())
val userKeycloak = authService.currentUser()
var user = userRepository.findByIdOrNull(userKeycloak.id)
if (user == null) {
val claims = getCurrentUserClaims()
user = User(
claims["sub"].toString(),
"@${claims["preferred_username"].toString()}:fosil.eu",
claims["given_name"].toString(),
claims["email"].toString()
)
user = userKeycloak.toUser()
CreateUser(userRepository).createUser(user)
}
@@ -41,7 +39,9 @@ class PrivateUserRestController(private val userRepository: UserRepository) : Ap
@RequestMapping("/create")
fun createUser(@RequestBody @Validated createUserDto: CreateUserDto): ApiResponse<Unit> {
return response {
CreateUser(userRepository).createUser(createUserDto.toUser(userRepository))
if (!authService.currentUser().admin)
throw InsufficientPermissions("Only admins can create users.")
CreateUser(userRepository).createUser(createUserDto.toUser())
}
}

View File

@@ -0,0 +1,36 @@
package eu.fosil.okupamicoche.spring.services
import eu.fosil.okupamicoche.entities.*
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.stereotype.Service
@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)
}
throw UserIdNotFoundException()
}
fun canEditTravel(travelId: TravelId?): Boolean {
val travel = travelRepository.findByIdOrNull(travelId) ?: return false
return currentUser().admin || currentUser().id == travel.driver.id
}
fun canEditUser(user: User): Boolean {
return currentUser().admin || currentUser().id == user.id
}
}