ktorio / ktor

Framework for quickly creating connected applications in Kotlin with minimal effort
https://ktor.io
Apache License 2.0
13.01k stars 1.06k forks source link

Recomendations for ACL? #807

Closed chrisjenx closed 3 years ago

chrisjenx commented 5 years ago

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:

location<Route> {
 interceptUserIsLoggedIn()
}

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?

cy6erGn0m commented 5 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

chrisjenx commented 5 years ago

@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!

otcdlink-simpleuser commented 5 years ago

@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.

otcdlink-simpleuser commented 5 years ago

@chrisjenx I've taken inspiration from the following:

SuuSoJeat commented 5 years ago

Does Ktor have any plan to support RBAC out of the box in the near future?

cy6erGn0m commented 5 years ago

@SuuSoJeat not near future unfortunately unless somebody contribute design

e5l commented 3 years ago

Closed as answered