apply BasicAuth to all matchers

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

apply BasicAuth to all matchers

devnewb
Hi Guys.

Is there a way I can apply BasicAuth to all my matchers (resources) ?

Thanks.
Reply | Threaded
Open this post in threaded view
|

Re: apply BasicAuth to all matchers

n8han
Administrator
Yep, two ways:
http://sourced.implicit.ly/net.databinder/dispatch-http/0.7.7/dispatch/Http.scala.html#19526

"as" and "as_!", difference explained in the method comments.

Nathan

On 11/19/10 1:23 PM, devnewb [via Databinder] wrote:
Hi Guys.

Is there a way I can apply BasicAuth to all my matchers (resources) ?

Thanks.


View message @ http://databinder.3617998.n2.nabble.com/apply-BasicAuth-to-all-matchers-tp5755629p5755629.html
To start a new topic under Unfiltered, email [hidden email]
To unsubscribe from Unfiltered, click here.

Reply | Threaded
Open this post in threaded view
|

Re: apply BasicAuth to all matchers

n8han
Administrator
Sorry I misread, thought this was on the dispatch list.
Reply | Threaded
Open this post in threaded view
|

Re: apply BasicAuth to all matchers

n8han
Administrator
In reply to this post by devnewb
So, I'll try this again with my brain being a little less loopy from a cold. You may have found the BasicAuth extractor:
https://github.com/n8han/Unfiltered/blob/master/library/src/main/scala/request/auths.scala#L4

If you want to apply this or any other extractor to every request, one option is to compose pattern matching expressions. This is pretty common on a small scale in unfiltered but it could also be done effectively in a bigger way. Here's some code I won't try to compile but that should be basically right:

class App extends unfiltered.filter.Plan {
  def intent = {
    case BasicAuth((name, pass), req) =>
      if (verify(name, pass))
        AuthorizedApp.intent(req)
      else NotAuthorized
    case _ => NotAuthorized
}
object AuthorizedApp {
  def intent: unfiltered.filter.Plan.Intent = {
    case GET(...) =>
  }
}

Or you could compose in many other ways. It might be interesting to have an object that extends unfiltered.filter.Plan.Intent directly and does the processing in apply(), but I can't say offhand if that would even work.

The other way to handle this is as the servlet filter api would traditionally have you do it. Create one filter just for authorization, return Pass on successful authorization and NotAuthorized on any failure. Add another filter to the chain after that which would be similar to AuthorizedApp above, except that it would be an actual Filter by extending unfiltered.filter.Plan.

Nathan
Reply | Threaded
Open this post in threaded view
|

Re: apply BasicAuth to all matchers

soft_props
I've generally followed the second approach in a few apps I've worked on. I usually have a class that I use just for Auth and take advantage of the filter chain so I can reuse the same code in other apps. I just put this auth plan before other plans in the filter chain. I think this is generally a good practice because it helps keep concerns separated and allows you to more easily swap out implementations of how you choose to handle authentication in one place.

Below is a somewhat simple shorthand for how to do that

/** handles authentication. no app logic */
class Auth(authSvc: AuthSvc) extends unfiltered.filter.Plan {
  def intent = {
    case BasicAuth((name, pass), _) =>
      if (authSvc.auth(name, pass)) Pass
      else NotAuthorized
    case _ => NotAuthorized
  }
}

/** handles app logic. no auth logic */
class App(otherSvcs: ...) extends unfiltered.filter.Plan{
  def intent = {
    case GET(...) =>
  }
}

/** serves app */
object Server {
  def main(args: Array[String]) {
    // bootstrap app deps...
     val authSvc = // ...
     unfiltered.jetty.Http(8080).filter(new Auth(authSvc)).filter(new App(...)).run
  }
}


In separating the concerns you can be a little more flexible in how you piece together your app modules.
Reply | Threaded
Open this post in threaded view
|

Re: apply BasicAuth to all matchers

soft_props
Also keep in mind most of unfiltered response handlers can be combined with ~>.


I probably should have also mentioned n cases where you are handling basic auth you wanted to combine Unauthorized with the WWW-Authenticate header. You can do this with unfiltered by returning something like

Unauthorized ~> WWWAuthenticate("Basic realm=\"/\"")

So thing I might suggest augmenting my example above with something like

class Auth(authSvc: AuthSvc) extends unfiltered.filter.Plan {
  val AuthFail = Unauthorized ~> WWWAuthenticate("Basic realm=\"/\"")
  def intent = {
    case BasicAuth((name, pass), _) =>
      if (authSvc.auth(name, pass)) Pass
      else AuthFail
    case _ => AuthFail
  }
}


I've considered combining them before with a higher level response function because the spec suggests they should always go together [1]. For now you can just use the ~> combinator operator.

Check out the available headers [2] and status codes [3] for more info

[1]: http://tools.ietf.org/html/rfc2616#section-10.4.2
[2]: http://sourced.implicit.ly/net.databinder/unfiltered/0.2.2/response/headers.scala.html
[3]: http://sourced.implicit.ly/net.databinder/unfiltered/0.2.2/response/statuses.scala.html
Reply | Threaded
Open this post in threaded view
|

Re: apply BasicAuth to all matchers

devnewb
Thanks for your help guys.  Love the framework.  Will try this out when back from holiday.
Reply | Threaded
Open this post in threaded view
|

Re: apply BasicAuth to all matchers

soft_props
Thanks for the support devnewb.
Reply | Threaded
Open this post in threaded view
|

Re: apply BasicAuth to all matchers

devnewb
In reply to this post by soft_props
Hi.

I tried to replicate your suggestion via filters in web.xml but am struggling.  I have not tried it in code like your example, do you use:

unfiltered.jetty.Http(8080).filter...

in production ?

Thanks.
Reply | Threaded
Open this post in threaded view
|

Re: apply BasicAuth to all matchers

soft_props
I use unfiltered for a few internal tools at work using the unfiltered Http server abstraction but I am using what unfiltered.jetty does internally in java projects that are in production. Internally unfiltered.jetty.Http is just setting up a connector to listen on a port and a builds up a set of servlet context handlers.

I'll try to write up an example app and post to github with an example of how you can use basic auth tonight and post back here
Reply | Threaded
Open this post in threaded view
|

Re: apply BasicAuth to all matchers

soft_props
I just made a quick g8 template for basic auth handling that basically extends the vanilla unfiltered template
if you have giterate [1] installed.

g8 softprops/unfiltered-basic-auth

otherwise you can just check out the source on github

here is a minimal spec https://github.com/softprops/unfiltered-basic-auth.g8/blob/master/src/main/g8/src/test/scala/ExampleSpec.scala#L19-35

and here is where I handle the filtering https://github.com/softprops/unfiltered-basic-auth.g8/blob/master/src/main/g8/src/main/scala/Example.scala#L53-85

[1]: https://github.com/n8han/giter8#readme

let me know if that helps
Reply | Threaded
Open this post in threaded view
|

Re: apply BasicAuth to all matchers

devnewb
Awesome, thanks for your help, I will check this out tommorow!  Suffering from a cold atm.

Thanks!
Reply | Threaded
Open this post in threaded view
|

Re: apply BasicAuth to all matchers

devnewb
In reply to this post by soft_props
Had to get stuck in and have a look at this.  All works for me, thanks for your help!  Thanks for introducing me to Giter8 too, slick.