wiro
wiro copied to clipboard
Wiro middlewares
This is a proposal for a pluggable middleware system for wiro.
I'll keep it short, and we can flesh it out with more details if there's interest.
Currently
HTTP Request -> Controller
Proposed
HTTP Request -> Middleware -> Controller
A middleware can be a function
WiroContext => Future[E, A]
WiroContext
would include the name of the controller method being called and any additional metadata, like all the annotations on the method.
For instance, given
@path("world")
trait WorldController {
@command
@role(Admin)
def destroyTheWorld(w: World): Future[MyError, Nothing]
}
WiroContext
could look like:
WiroContext(
method = "destroyTheWorld",
path = "world",
httpHeaders = Map(
"Authorization" -> "Token token=123123123123123123"
),
annotations = List(
WiroAnnotation(key = "command"),
WiroAnnotation(key = "role", params = List("Admin")
)
)
This would allow to implement a middleware that performs authorization:
def authorizationMiddleware(ctx: WiroContext): Future[MyError, Unit] = for {
token <- parseToken(ctx)
user <- findUserByToken(token)
_ <- checkUserIsAuthorized(user, ctx.annotations.find(_.key == "role"))
} yield ()
Still a few sketchy points, but it could be useful for many situations.
💡 Instead of Unit
, it could be more useful to be able to pass the yield right (authorized) result back to the controller route.
This way you can make optimizations (recycling some work already done while authorizing the request) and more in general deal with any reasonable side effect of authorization.
(edit: I was thinking about authorization, but the same reasoning applies to different contexts)
Also, WiroContext should contain the route input values (w: World
in your examples).