@@ -29,6 +29,7 @@ import io.github.smiley4.ktoropenapi.post
2929import io.github.smiley4.ktoropenapi.put
3030
3131import io.ktor.server.application.ApplicationCall
32+ import io.ktor.server.auth.principal
3233import io.ktor.server.routing.Route
3334import io.ktor.server.routing.RouteSelector
3435import io.ktor.server.routing.RouteSelectorEvaluation
@@ -51,17 +52,13 @@ suspend fun ApplicationCall.createAuthorizedPrincipal(
5152 (this as ? RoutingPipelineCall )?.let { routingCall ->
5253 val checker = routingCall.route.findAuthorizationChecker()
5354
54- val effectiveRole = if (checker != null ) {
55- checker.loadEffectiveRole(
56- service = authorizationService,
57- userId = payload.getClaim(" preferred_username" ).asString(),
58- call = this
59- ).takeIf { checker.checkAuthorization(it) }
60- } else {
61- EffectiveRole .EMPTY
62- }
55+ val effectiveRole = checker?.loadEffectiveRole(
56+ service = authorizationService,
57+ userId = payload.getClaim(" preferred_username" ).asString(),
58+ call = this
59+ ) ? : EffectiveRole .EMPTY
6360
64- effectiveRole?. let { OrtServerPrincipal .create(payload, it) }
61+ OrtServerPrincipal .create(payload, effectiveRole)
6562 }
6663
6764/* *
@@ -71,7 +68,7 @@ fun Route.get(
7168 builder : RouteConfig .() -> Unit ,
7269 checker : AuthorizationChecker ,
7370 body : suspend RoutingContext .() -> Unit
74- ): Route = documentedAuthorized(checker) { get(builder, body ) }
71+ ): Route = documentedAuthorized(checker, body ) { get(builder, it ) }
7572
7673/* *
7774 * Create a new [Route] for HTTP POST requests that performs an automatic authorization check using the given [checker].
@@ -80,7 +77,7 @@ fun Route.post(
8077 builder : RouteConfig .() -> Unit ,
8178 checker : AuthorizationChecker ,
8279 body : suspend RoutingContext .() -> Unit
83- ): Route = documentedAuthorized(checker) { post(builder, body ) }
80+ ): Route = documentedAuthorized(checker, body ) { post(builder, it ) }
8481
8582/* *
8683 * Create a new [Route] for HTTP PATCH requests that performs an automatic authorization check using the given
@@ -90,7 +87,7 @@ fun Route.patch(
9087 builder : RouteConfig .() -> Unit ,
9188 checker : AuthorizationChecker ,
9289 body : suspend RoutingContext .() -> Unit
93- ): Route = documentedAuthorized(checker) { patch(builder, body ) }
90+ ): Route = documentedAuthorized(checker, body ) { patch(builder, it ) }
9491
9592/* *
9693 * Create a new [Route] for HTTP PUT requests that performs an automatic authorization check using the given
@@ -100,7 +97,7 @@ fun Route.put(
10097 builder : RouteConfig .() -> Unit ,
10198 checker : AuthorizationChecker ,
10299 body : suspend RoutingContext .() -> Unit
103- ): Route = documentedAuthorized(checker) { put(builder, body ) }
100+ ): Route = documentedAuthorized(checker, body ) { put(builder, it ) }
104101
105102/* *
106103 * Create a new [Route] for HTTP DELETE requests that performs an automatic authorization check using the given
@@ -110,16 +107,31 @@ fun Route.delete(
110107 builder : RouteConfig .() -> Unit ,
111108 checker : AuthorizationChecker ,
112109 body : suspend RoutingContext .() -> Unit
113- ): Route = documentedAuthorized(checker) { delete(builder, body ) }
110+ ): Route = documentedAuthorized(checker, body ) { delete(builder, it ) }
114111
115112/* *
116113 * Generic function to create a new [Route] that performs an automatic authorization check using the given [checker].
117- * The content of the route is defined by the given [build] function.
114+ * The content of the route is defined by the given original [body] and the [build] function.
118115 */
119- private fun Route.documentedAuthorized (checker : AuthorizationChecker , build : Route .() -> Unit ): Route {
116+ private fun Route.documentedAuthorized (
117+ checker : AuthorizationChecker ,
118+ body : suspend RoutingContext .() -> Unit ,
119+ build : Route .(suspend RoutingContext .() -> Unit ) -> Unit
120+ ): Route {
120121 val authorizedRoute = createChild(authorizedRouteSelector(checker.toString()))
121122 authorizedRoute.attributes.put(AuthorizationCheckerKey , checker)
122- authorizedRoute.build()
123+
124+ val authorizedBody: suspend RoutingContext .() -> Unit = {
125+ val principal = call.principal<OrtServerPrincipal >() ? : throw AuthorizationException ()
126+
127+ if (! checker.checkAuthorization(principal.effectiveRole)) {
128+ throw AuthorizationException ()
129+ }
130+
131+ body()
132+ }
133+
134+ authorizedRoute.build(authorizedBody)
123135 return authorizedRoute
124136}
125137
@@ -160,3 +172,9 @@ object AuthenticationProviders {
160172 */
161173 const val TOKEN_PROVIDER = " token"
162174}
175+
176+ /* *
177+ * An exception class to indicate a failed authorization check. Such exceptions are thrown by the route functions when
178+ * the current user does not have the required permissions. They are mapped to HTTP 403 Forbidden responses.
179+ */
180+ class AuthorizationException : RuntimeException ()
0 commit comments