mirror of
https://github.com/2martens/tsw-timetable.git
synced 2026-10-17 07:57:55 +02:00
fix: Check for subject not authorized party
This commit is contained in:
@@ -63,9 +63,8 @@ class UserController(
|
|||||||
)
|
)
|
||||||
]) body: User
|
]) body: User
|
||||||
): ResponseEntity<User> {
|
): ResponseEntity<User> {
|
||||||
val authentication = SecurityContextHolder.getContext().authentication
|
val authentication = SecurityContextHolder.getContext().authentication as JwtAuthenticationToken
|
||||||
val jwt = authentication as JwtAuthenticationToken
|
val subject = authentication.token.getClaimAsString("sub") ?: authentication.name
|
||||||
val authorizedParty = jwt.token.getClaimAsString("azp")
|
|
||||||
var created = false
|
var created = false
|
||||||
|
|
||||||
val userIdConverted = UserId.of(NonEmptyString(userId))
|
val userIdConverted = UserId.of(NonEmptyString(userId))
|
||||||
@@ -73,7 +72,7 @@ class UserController(
|
|||||||
if (user == null) {
|
if (user == null) {
|
||||||
created = true
|
created = true
|
||||||
user = mapper.mapToDB(body)
|
user = mapper.mapToDB(body)
|
||||||
} else if (authorizedParty == userId) {
|
} else if (subject == userId) {
|
||||||
user.name = NonEmptyString(body.name)
|
user.name = NonEmptyString(body.name)
|
||||||
user.email = Email.of(NonEmptyString(body.email))
|
user.email = Email.of(NonEmptyString(body.email))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+4
-4
@@ -1,7 +1,7 @@
|
|||||||
package de.twomartens.timetable.configuration
|
package de.twomartens.timetable.configuration
|
||||||
|
|
||||||
import de.twomartens.timetable.configuration.roles.CheckParty
|
import de.twomartens.timetable.configuration.roles.CheckSubject
|
||||||
import de.twomartens.timetable.configuration.roles.PartyAuthorizationManager
|
import de.twomartens.timetable.configuration.roles.SubjectAuthorizationManager
|
||||||
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut
|
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut
|
||||||
import org.springframework.beans.factory.config.BeanDefinition.ROLE_INFRASTRUCTURE
|
import org.springframework.beans.factory.config.BeanDefinition.ROLE_INFRASTRUCTURE
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
@@ -16,8 +16,8 @@ class AuthorizationManagerConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@Role(ROLE_INFRASTRUCTURE)
|
@Role(ROLE_INFRASTRUCTURE)
|
||||||
fun checkPartyInterceptor(): AuthorizationManagerBeforeMethodInterceptor {
|
fun checkPartyInterceptor(): AuthorizationManagerBeforeMethodInterceptor {
|
||||||
val pointcut = AnnotationMatchingPointcut(null, CheckParty::class.java, true)
|
val pointcut = AnnotationMatchingPointcut(null, CheckSubject::class.java, true)
|
||||||
val interceptor = AuthorizationManagerBeforeMethodInterceptor(pointcut, PartyAuthorizationManager())
|
val interceptor = AuthorizationManagerBeforeMethodInterceptor(pointcut, SubjectAuthorizationManager())
|
||||||
interceptor.order = AuthorizationInterceptorsOrder.PRE_AUTHORIZE.order
|
interceptor.order = AuthorizationInterceptorsOrder.PRE_AUTHORIZE.order
|
||||||
return interceptor
|
return interceptor
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -6,6 +6,6 @@ import java.lang.annotation.Inherited
|
|||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
@Inherited
|
@Inherited
|
||||||
@MustBeDocumented
|
@MustBeDocumented
|
||||||
annotation class CheckParty(
|
annotation class CheckSubject(
|
||||||
val partyParamName: String // Name of method argument holding the party ID
|
val partyParamName: String // Name of method argument holding the party ID
|
||||||
)
|
)
|
||||||
+7
-6
@@ -7,20 +7,21 @@ import org.springframework.security.authorization.AuthorizationManager
|
|||||||
import org.springframework.security.authorization.AuthorizationResult
|
import org.springframework.security.authorization.AuthorizationResult
|
||||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler
|
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler
|
||||||
import org.springframework.security.core.Authentication
|
import org.springframework.security.core.Authentication
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
class PartyAuthorizationManager : AuthorizationManager<MethodInvocation>, MethodAuthorizationDeniedHandler {
|
class SubjectAuthorizationManager : AuthorizationManager<MethodInvocation>, MethodAuthorizationDeniedHandler {
|
||||||
@Deprecated("Deprecated in Spring Security")
|
@Deprecated("Deprecated in Spring Security")
|
||||||
override fun check(authentication: Supplier<Authentication?>?, invocation: MethodInvocation?): AuthorizationDecision {
|
override fun check(authentication: Supplier<Authentication?>?, invocation: MethodInvocation?): AuthorizationDecision {
|
||||||
if (authentication == null || authentication.get() == null || !authentication.get()!!.isAuthenticated) {
|
if (authentication == null || authentication.get() == null || !authentication.get()!!.isAuthenticated) {
|
||||||
return AuthorizationDecision(false)
|
return AuthorizationDecision(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val annotation = invocation!!.method.getAnnotation(CheckParty::class.java)
|
val annotation = invocation!!.method.getAnnotation(CheckSubject::class.java)
|
||||||
?: invocation.method.declaringClass.getAnnotation(CheckParty::class.java)
|
?: invocation.method.declaringClass.getAnnotation(CheckSubject::class.java)
|
||||||
?: return AuthorizationDecision(true) // No annotation, allow
|
?: return AuthorizationDecision(true) // No annotation, allow
|
||||||
|
|
||||||
val authentication = authentication.get()!!
|
val authentication = authentication.get()!! as JwtAuthenticationToken
|
||||||
val partyParamName = annotation.partyParamName
|
val partyParamName = annotation.partyParamName
|
||||||
|
|
||||||
// 1. Authorized party check
|
// 1. Authorized party check
|
||||||
@@ -31,13 +32,13 @@ class PartyAuthorizationManager : AuthorizationManager<MethodInvocation>, Method
|
|||||||
}
|
}
|
||||||
|
|
||||||
val requestedPartyId = invocation.arguments[argIndex]?.toString()
|
val requestedPartyId = invocation.arguments[argIndex]?.toString()
|
||||||
val tokenPartyId = (authentication.details as? Map<*, *>)?.get("azp")?.toString()
|
val tokenPartyId = authentication.token.getClaimAsString("sub")
|
||||||
?: authentication.name // fallback
|
?: authentication.name // fallback
|
||||||
|
|
||||||
return AuthorizationDecision(requestedPartyId == tokenPartyId)
|
return AuthorizationDecision(requestedPartyId == tokenPartyId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleDeniedInvocation(methodInvocation: MethodInvocation?, authorizationResult: AuthorizationResult?): Any? {
|
override fun handleDeniedInvocation(methodInvocation: MethodInvocation?, authorizationResult: AuthorizationResult?): Any? {
|
||||||
throw AuthorizationDeniedException("The requested resources are not accessible to the authorized party")
|
throw AuthorizationDeniedException("The requested resources are not accessible for the subject")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+5
-5
@@ -1,7 +1,7 @@
|
|||||||
package de.twomartens.timetable.formation
|
package de.twomartens.timetable.formation
|
||||||
|
|
||||||
import de.twomartens.timetable.auth.UserRepository
|
import de.twomartens.timetable.auth.UserRepository
|
||||||
import de.twomartens.timetable.configuration.roles.CheckParty
|
import de.twomartens.timetable.configuration.roles.CheckSubject
|
||||||
import de.twomartens.timetable.model.common.FormationId
|
import de.twomartens.timetable.model.common.FormationId
|
||||||
import de.twomartens.timetable.model.common.UserId
|
import de.twomartens.timetable.model.common.UserId
|
||||||
import de.twomartens.timetable.model.dto.Formation
|
import de.twomartens.timetable.model.dto.Formation
|
||||||
@@ -45,7 +45,7 @@ class FormationController(
|
|||||||
)
|
)
|
||||||
@SecurityRequirement(name = "bearer")
|
@SecurityRequirement(name = "bearer")
|
||||||
@SecurityRequirement(name = "oauth2")
|
@SecurityRequirement(name = "oauth2")
|
||||||
@CheckParty(partyParamName = "userId")
|
@CheckSubject(partyParamName = "userId")
|
||||||
@GetMapping("/{userId}/")
|
@GetMapping("/{userId}/")
|
||||||
fun getFormations(
|
fun getFormations(
|
||||||
@PathVariable @Parameter(description = "The id of the user",
|
@PathVariable @Parameter(description = "The id of the user",
|
||||||
@@ -98,7 +98,7 @@ class FormationController(
|
|||||||
)
|
)
|
||||||
@SecurityRequirement(name = "bearer")
|
@SecurityRequirement(name = "bearer")
|
||||||
@SecurityRequirement(name = "oauth2")
|
@SecurityRequirement(name = "oauth2")
|
||||||
@CheckParty(partyParamName = "userId")
|
@CheckSubject(partyParamName = "userId")
|
||||||
@GetMapping("/{userId}/{id}")
|
@GetMapping("/{userId}/{id}")
|
||||||
fun getFormation(
|
fun getFormation(
|
||||||
@PathVariable @Parameter(description = "The id of the user",
|
@PathVariable @Parameter(description = "The id of the user",
|
||||||
@@ -147,7 +147,7 @@ class FormationController(
|
|||||||
)
|
)
|
||||||
@SecurityRequirement(name = "bearer")
|
@SecurityRequirement(name = "bearer")
|
||||||
@SecurityRequirement(name = "oauth2")
|
@SecurityRequirement(name = "oauth2")
|
||||||
@CheckParty(partyParamName = "userId")
|
@CheckSubject(partyParamName = "userId")
|
||||||
@PutMapping("/{userId}/{id}")
|
@PutMapping("/{userId}/{id}")
|
||||||
fun putFormation(
|
fun putFormation(
|
||||||
@PathVariable @Parameter(description = "The id of the user",
|
@PathVariable @Parameter(description = "The id of the user",
|
||||||
@@ -240,7 +240,7 @@ class FormationController(
|
|||||||
)
|
)
|
||||||
@SecurityRequirement(name = "bearer")
|
@SecurityRequirement(name = "bearer")
|
||||||
@SecurityRequirement(name = "oauth2")
|
@SecurityRequirement(name = "oauth2")
|
||||||
@CheckParty(partyParamName = "userId")
|
@CheckSubject(partyParamName = "userId")
|
||||||
@DeleteMapping("/{userId}/{id}")
|
@DeleteMapping("/{userId}/{id}")
|
||||||
fun deleteFormation(
|
fun deleteFormation(
|
||||||
@PathVariable @Parameter(description = "The id of the user",
|
@PathVariable @Parameter(description = "The id of the user",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package de.twomartens.timetable.route
|
package de.twomartens.timetable.route
|
||||||
|
|
||||||
import de.twomartens.timetable.auth.UserRepository
|
import de.twomartens.timetable.auth.UserRepository
|
||||||
import de.twomartens.timetable.configuration.roles.CheckParty
|
import de.twomartens.timetable.configuration.roles.CheckSubject
|
||||||
import de.twomartens.timetable.model.common.RouteId
|
import de.twomartens.timetable.model.common.RouteId
|
||||||
import de.twomartens.timetable.model.common.UserId
|
import de.twomartens.timetable.model.common.UserId
|
||||||
import de.twomartens.timetable.model.dto.TswRoute
|
import de.twomartens.timetable.model.dto.TswRoute
|
||||||
@@ -44,7 +44,7 @@ class RouteController(
|
|||||||
)
|
)
|
||||||
@SecurityRequirement(name = "bearer")
|
@SecurityRequirement(name = "bearer")
|
||||||
@SecurityRequirement(name = "oauth2")
|
@SecurityRequirement(name = "oauth2")
|
||||||
@CheckParty(partyParamName = "userId")
|
@CheckSubject(partyParamName = "userId")
|
||||||
@GetMapping("/{userId}/")
|
@GetMapping("/{userId}/")
|
||||||
fun getRoutes(
|
fun getRoutes(
|
||||||
@PathVariable @Parameter(description = "The id of the user",
|
@PathVariable @Parameter(description = "The id of the user",
|
||||||
@@ -89,7 +89,7 @@ class RouteController(
|
|||||||
)
|
)
|
||||||
@SecurityRequirement(name = "bearer")
|
@SecurityRequirement(name = "bearer")
|
||||||
@SecurityRequirement(name = "oauth2")
|
@SecurityRequirement(name = "oauth2")
|
||||||
@CheckParty(partyParamName = "userId")
|
@CheckSubject(partyParamName = "userId")
|
||||||
@GetMapping("/{userId}/{id}")
|
@GetMapping("/{userId}/{id}")
|
||||||
fun getRoute(
|
fun getRoute(
|
||||||
@PathVariable @Parameter(description = "The id of the user",
|
@PathVariable @Parameter(description = "The id of the user",
|
||||||
@@ -133,7 +133,7 @@ class RouteController(
|
|||||||
)
|
)
|
||||||
@SecurityRequirement(name = "bearer")
|
@SecurityRequirement(name = "bearer")
|
||||||
@SecurityRequirement(name = "oauth2")
|
@SecurityRequirement(name = "oauth2")
|
||||||
@CheckParty(partyParamName = "userId")
|
@CheckSubject(partyParamName = "userId")
|
||||||
@PutMapping("/{userId}/{id}")
|
@PutMapping("/{userId}/{id}")
|
||||||
fun putRoute(
|
fun putRoute(
|
||||||
@PathVariable @Parameter(description = "The id of the user",
|
@PathVariable @Parameter(description = "The id of the user",
|
||||||
@@ -199,7 +199,7 @@ class RouteController(
|
|||||||
)
|
)
|
||||||
@SecurityRequirement(name = "bearer")
|
@SecurityRequirement(name = "bearer")
|
||||||
@SecurityRequirement(name = "oauth2")
|
@SecurityRequirement(name = "oauth2")
|
||||||
@CheckParty(partyParamName = "userId")
|
@CheckSubject(partyParamName = "userId")
|
||||||
@DeleteMapping("/{userId}/{id}")
|
@DeleteMapping("/{userId}/{id}")
|
||||||
fun deleteRoute(
|
fun deleteRoute(
|
||||||
@PathVariable @Parameter(description = "The id of the user",
|
@PathVariable @Parameter(description = "The id of the user",
|
||||||
|
|||||||
Reference in New Issue
Block a user