wiro icon indicating copy to clipboard operation
wiro copied to clipboard

Wiro middlewares

Open gabro opened this issue 7 years ago • 2 comments

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.

gabro avatar Oct 06 '17 09:10 gabro

💡 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)

bytecodeguru avatar Oct 06 '17 11:10 bytecodeguru

Also, WiroContext should contain the route input values (w: World in your examples).

bytecodeguru avatar Oct 06 '17 11:10 bytecodeguru