Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] AuthedContext implementation #184

Merged
merged 6 commits into from
Jul 19, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions core/src/main/scala/org/http4s/rho/AuthedRhoService.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.http4s
package rho


/** The [[AuthedRhoService]] provides a convenient way to define a RhoService
* which works with http4s authentication middleware.
* {{{
* case class User(name: String, id: UUID)
*
* object Auth {
* val authUser: Service[Request, User] = Kleisli({ _ =>
* Task.now(User("Test User", UUID.randomUUID()))
* })
*
* val authenticated = AuthMiddleware(authUser)
* }
*
* object MyAuth extends AuthedRhoService[User]
*
* object MyService extends RhoService {
* import MyAuth._
* GET +? param("foo", "bar") |>> { (req: Request, foo: String) =>
* val user = getAuth(req)
* if (user.name == "Test User") {
* Ok(s"just root with parameter 'foo=$foo'")
* } else {
* BadRequest("This should not have happened.")
* }
* }
* }
*
* val service = Auth.authenticated(MyAuth.toService(MyService))
* }}}
*
* @tparam U authInfo type for this service.
*/
class AuthedRhoService[U] {

/* Attribute key to lookup authInfo in request attributeMap . */
final private val authKey = AttributeKey[U]("authInfo")

/** Turn the [[HttpService]] into an `AuthedService`
*
* @param rhoService [[RhoService]] to convert
* @return An `AuthedService` which can be mounted by http4s servers.
*/
def toService(rhoService: RhoService): AuthedService[U] = {
val service = rhoService.toService()
Service.lift { case AuthedRequest(authInfo, req) =>
service(req.withAttribute[U](authKey, authInfo))
}
}

/* Get the authInfo object from request. */
def getAuth(req: Request): U = {
req.attributes.get[U](authKey).get
}
}

55 changes: 55 additions & 0 deletions core/src/test/scala/org/http4s/rho/AuthedRhoServiceSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.http4s
package rho

import java.util.UUID

import cats.data._
import fs2.Task
import org.http4s.server.AuthMiddleware
import org.specs2.mutable.Specification
import scodec.bits.ByteVector


case class User(name: String, id: UUID)

object Auth {

val authUser: Service[Request, User] = Kleisli({ _ =>
Task.now(User("Test User", UUID.randomUUID()))
})

val authenticated = AuthMiddleware(authUser)
}


object MyAuth extends AuthedRhoService[User]

object MyService extends RhoService {
import MyAuth._

GET +? param("foo", "bar") |>> { (req: Request, foo: String) =>
val user = getAuth(req)
if (user.name == "Test User") {
Ok(s"just root with parameter 'foo=$foo'")
} else {
BadRequest("This should not have happened.")
}
}
}

class AuthedRhoServiceSpec extends Specification {

val service = Auth.authenticated(MyAuth.toService(MyService))

"AuthedRhoService execution" should {

"Be able to have access to authInfo" in {
val request = Request(Method.GET, Uri(path = "/"))
val resp = service.run(request).unsafeRun().orNotFound
if (resp.status == Status.Ok) {
val body = new String(resp.body.runLog.unsafeRun.foldLeft(ByteVector.empty)(_ :+ _).toArray)
body should_== "just root with parameter 'foo=bar'"
} else sys.error(s"Invalid response code: ${resp.status}")
}
}
}