Created
February 11, 2014 19:10
-
-
Save ian-kent/8941847 to your computer and use it in GitHub Desktop.
Akka based authentication and authorisation for Play Framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package wrappers | |
import play.api._ | |
import play.api.mvc._ | |
import scala.concurrent._ | |
import scala.concurrent.Future | |
import play.mvc.Http.Status | |
import ExecutionContext.Implicits.global | |
import play.libs.Akka | |
import akka.actor.{Actor, Props} | |
import akka.pattern.ask | |
import akka.util.Timeout | |
import scala.concurrent.duration._ | |
import play.api.libs.json.{JsObject, Json} | |
// Authenticated action builder | |
object Authenticated extends ActionBuilder[AuthenticatedRequest] { | |
val authenticationActor = Akka.system.actorOf(Props[Authenticator], name = "authentication") | |
implicit val timeout = Timeout(1 second) | |
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = { | |
(authenticationActor ask Authenticate(request)).mapTo[AuthenticationResult] flatMap { result => | |
if(result.valid) | |
block(AuthenticatedRequest[A](result.user, request)) | |
else | |
block(AuthenticatedRequest[A](None, request)) | |
} recover { | |
case e => Results.Status(Status.INTERNAL_SERVER_ERROR) | |
} | |
} | |
} | |
// Authorised helpers | |
object Authorised { | |
def async[T](request: Request[T], user: Option[JsObject]) = (block: Future[SimpleResult]) => new Authorised[T](request, user, block) | |
def apply[T](request: Request[T], user: Option[JsObject]) = (block: SimpleResult) => new Authorised[T](request, user, future{block}) | |
val authorisationActor = Akka.system.actorOf(Props[Authorisor], name = "authorisation") | |
} | |
class Authorised[T](request: Request[T], user: Option[JsObject], success: Future[SimpleResult]) { | |
implicit val timeout = Timeout(1 second) | |
def authorised = { | |
(Authorised.authorisationActor ask Authorise(request)).mapTo[AuthorisationResult] map { result => | |
result.valid | |
} recover { | |
case e => false | |
} | |
} | |
def otherwise(block: => Future[SimpleResult]) : Future[SimpleResult] = authorised.flatMap { valid => if (valid) success else block } | |
def otherwise(block: => SimpleResult) : SimpleResult = if(authorised.value.get.get) success.value.get.get else block | |
} | |
// AuthenticatedRequest wrapper | |
trait AuthenticatedRequest[+A] extends Request[A] { | |
val user: Option[JsObject] | |
} | |
object AuthenticatedRequest { | |
def apply[A](u: Option[JsObject], r: Request[A]) = new AuthenticatedRequest[A] { | |
def id = r.id | |
def tags = r.tags | |
def uri = r.uri | |
def path = r.path | |
def method = r.method | |
def version = r.version | |
def queryString = r.queryString | |
def headers = r.headers | |
lazy val remoteAddress = r.remoteAddress | |
def username = None | |
val body = r.body | |
val user = u | |
} | |
} | |
// Case classes for communication using Akka | |
case class Authenticate[A](request: Request[A]) | |
case class AuthenticationResult(valid: Boolean = false, user: Option[JsObject] = None) | |
case class Authorise[A](request: Request[A]) | |
case class AuthorisationResult(valid: Boolean = false) | |
// Actor responsible for authentication | |
class Authenticator extends Actor { | |
def receive = { | |
case Authenticate(request) => sender ! AuthenticationResult(valid = true, user = Some(Json.obj())) | |
case _ => sender ! AuthenticationResult | |
} | |
} | |
// Actor responsible for authorisation | |
class Authorisor extends Actor { | |
def receive = { | |
case Authorise(request) => sender ! AuthorisationResult(valid = true) | |
case _ => sender ! AuthorisationResult | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, can you please explain from where is that
future
came inside Authorised.apply block