Hi guys I created an extension method to use in m...
# ktor
q
Hi guys I created an extension method to use in my routes. Something like
Copy code
fun Route.withRestrictions(build: Route.() -> Unit): Route {
    val authorizedRoute = createChild(AuthenticationRouteSelector(listOf(PermissionPlugin)))
    authorizedRoute.install(PermissionPlugin)
    authorizedRoute.build()
    return authorizedRoute
}
So I can do something like.
Copy code
route("/path") {
  withRestrictions {
    ....
  }
}
I would like to get a header from the request and return it in the extension function build block, I've tried something like
Copy code
fun Route.withRestrictions(build: Route.(String) -> Unit): Route {
    val authorizedRoute = createChild(AuthenticationRouteSelector(listOf(PermissionPlugin)))
    authorizedRoute.install(PermissionPlugin)
    authorizedRoute.intercept(Plugins or Call) {
      val myHeader = call.request.headers["request"]
      if(myHeader == null) { 
        call.respond(BadRequest, "The header is not present")
        finish()
        return@intercept 
      }
      authorizedRoute.build(myHeader)
    }
    return authorizedRoute
}
So I could use the header value in my route logic like this.
Copy code
route("/path") {
  withRestrictions { myHeader ->
    ....
  }
}
Actually I've spend time on this and not been able to solve it... the example above didn't work, any idea how can I solve this?
a
The problem is that the
withRestrictions
is a route builder executed once when the server starts but the headers are sent on each request. So either
withRestrictions
can have children routes or it can handle the request.
💡 1
thank you color 1
r
I solved a similar authorization problem with a plugin like so:
Copy code
@Suppress("functionName")
fun JwtAuthorize(vararg requiredRoles: RoleName = emptyArray()) = createRouteScopedPlugin("JwtAuthorize") {
    on(AuthenticationChecked) { call ->
        val principal = call.principal<JWTPrincipal>()
        if (principal != null) {
            val userRoles = principal.getClaim("userRoles", Array<String>::class) ?: emptyArray()

            if (requiredRoles.any { role -> !userRoles.contains(role.value) }) {
                throw UnauthorizedException()
            }
        }
    }
}
And my authorization extension function:
Copy code
fun Route.authorize(vararg roles: RoleName = emptyArray(), handler: Route.() -> Unit) {
    authenticate {
        install(JwtAuthorize(*roles)) { handler() }
    }
}
This way I can simply add
authorize(the-role-required) { <route mapping here> }
and it validates authentication and RBAC authorization for the routes mapped.
Not sure if it applies to your use case, but if it doesn't hopefully it might give you some ideas 🙂
You could also create a plugin listening to OnCallReceived hook, that would give you the request details in the
call
object prior to being handled by the router