A more flexible Access Manager?
See original GitHub issueThe current access manager presumes access is based on Roles, and Roles are associated with specific endpoints:
app.routes {
path("/my-api/something") {
path("/widget") {
get(WidgetController::get, roles(MyRoles.COOL_PEOPLE))
}
}
}
I applaud the simplicity of this.
What about other access control that is not based on attributes of users? Access control based on IP address, or based on time of day? Or based on http headers?
I believe we can keep it simple while supporting more access control use cases. I also think we can do this without imposing specific access control mechanisms. There are many, and people should be able to bring their own (or plug something in if it is a standard).
What I propose is rather than passing in a set of Role
s with my endpoints, how about if I pass in something that implements a marker interface AccessRules
:
public interface AccessRules {}
public class WidgetAccessRules : AccessRules { ... }
val widgetAccessRules: WidgetAccessRules = ...
app.routes {
path("/my-api/something") {
path("/widget") {
get(WidgetController::get, widgetAccessRules)
}
}
}
Now I can define any access rules I wish – I can make them as simple or as complex as needed. Simplest might be Role based AccessRules:
class RoleBasedAccessRules : AccessRules {
val allowRoles: Set<Role>
}
And the AccessManager
would work some thing like this:
@FunctionalInterface
public interface AccessManager {
void manage(@NotNull Handler handler, @NotNull Context ctx, @NotNull Array<AccessRules> accessRules) throws Exception;
}
With this in place, now access rules could be placed anywhere along the path()
of a route:
val PUBLIC_ACCESS: AccessRules = ...
app.routes {
path("/api/public", PUBLIC_ACCESS) {
path("/login") {
post(LoginController::post)
}
}
path("/api", AUTHENTICATED_ACCESS) {
path("/customer/:customerId", CUSTOMER_ACCESS) {
get("/foo", FooController::get)
get("/bar", BarController::get)
crud("widget/:widgetId", CustomerWidgetController())
}
path("/superuser", SUPERUSER_ACCESS) {
path("/batcave") {
get(BatcaveController::get)
}
}
}
}
Now access rules can be customized however people want, or can implement existing access control designs. This would provide a more flexible way to satisfy the varied requests for “support my flavor of authorization”.
The simplest case would be a single, literal AccessRule
at the tip of the endpoint with a set of allowed Roles
, which would feel similar to what we have today, using roleAccess()
rather than role()
from SecurityUtil
:
public interface AccessRule {}
data class RoleAccess(val allowedRoles: Set<Role>) : AccessRule
fun roleAccess(vararg roles: Role) = RoleAccess(allowedRoles = setOf(*roles))
app.routes {
path("/my-api/something") {
path("/widget") {
get(WidgetController::get, roleAccess(MyRoles.COOL_PEOPLE))
}
}
}
Forgive me if my examples above are not all syntactically correct – I’m just trying to communicate the idea of supplying a list of generic AccessRules
as you go down a path
, and let the AccessManager
walk that list to decide what to do.
Looking for comments.
Issue Analytics
- State:
- Created 4 years ago
- Comments:11 (5 by maintainers)
Top GitHub Comments
I will expand a bit in the docs and mention filters a bit more.
By the way, since the ApiBuilder is static, you can also easily write your own methods to use in the
app.routes
DSL.Sounds like a good plan!