Closed chrisjenx closed 3 years ago
There is no ACL/roles control out of the box yet. However it seems that it's easy to emulate spring's like solution: make your principal with roles and a filtering interceptor with a list of rules. In theory you can even build a access rules route tree similar to the main routing
@cy6erGn0m interesting, if you have any examples of that I would be super interested. I guess my knowledge in this area is pretty limited so I don't know how Spring does it.
I kinda started going down this path where I wrap my routes:
fun Route.checkRouteHasUserSession(callback: Route.() -> Unit) : Route {
// With createChild, we create a child node for this received Route
val routeWithCheck = this.createChild(object : RouteSelector(1.0) {
override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation =
RouteSelectorEvaluation.Constant
})
// Intercepts calls from this route at the features step
routeWithCheck.intercept(ApplicationCallPipeline.Features) {
val session = getUserSession()
if (session == null) {
application.log.trace("Intercept: Denied (interceptUserHasSession)")
// If not logged in then can't go here
redirect(LoginPage())
finish()
}else {
proceed() // With proceed we can define code to be executed before and after the call
}
}
// Configure this route with the block provided by the user
callback(routeWithCheck)
return routeWithCheck
}
/**
* Authentication handled by AuthenticationInterceptors
*/
checkRouteHasUserSession {
get<UserProfilePage> { userPage ->
val user = call.getUserOrNull() ?: return@get
respondUserPage(user)
}
}
But that would get out of hand super quickly if I needed to do 2+ checks getting into nesting hell!
@chrisjenx this is how I do. I'm using some homegrown framework but the ideas can fit into Ktor.
Every request or change is encapsulated in a single Command
object. Commands are deserialized in the Netty pipeline. There is a some Command
interceptor at the end of the Netty pipeline for enforcing permissions.
Application code (not published) defines a UserBook
which contains every user and organisations and permissions. The UserBook
is an immutable object -- throw away the LDAP crap and use Kotlin data class instead!
The permission interceptor keeps a reference on the latest version of the UserBook
. More precisely there is one interceptor in each pipeline. So permissions are evaluated in parallel.
There is some kind of registry to associate a Command with appropriate permission evaluator, which has knowledge of Command parameters, and can evaluate them in depth basing on UserBook
. For instance it can enforce transitivity across nested organisations when it makes sense.
This looks tricky but as far as I know permissions always are.
@chrisjenx I've taken inspiration from the following:
Does Ktor have any plan to support RBAC out of the box in the near future?
@SuuSoJeat not near future unfortunately unless somebody contribute design
Closed as answered
Ktor Version
1.0.1
Ktor Engine Used(client or server and name)
Netty Server
JVM Version, Operating System and Relevant Context
JVM 1.8.0_181
Feedback
This isn't a bug, more of a question/feature request. How do people handle access control.
For example, users only being able to access/edit their profile page, admins being able to access everywhere?
I started doing this in each get/post but felt like I was dumping access control responsibility on the controllers. So then I started adding intercepts for each route:
My issue with that is now there feels like very loose coupling as I have my Access Control somewhere completely different.
Thoughts on best practices around this?