Skip to content

Instantly share code, notes, and snippets.

@ian-kent
Created February 11, 2014 19:10
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save ian-kent/8941847 to your computer and use it in GitHub Desktop.
Save ian-kent/8941847 to your computer and use it in GitHub Desktop.
Akka based authentication and authorisation for Play Framework
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
}
}
@sosuren
Copy link

sosuren commented Feb 25, 2015

Hi, can you please explain from where is that future came inside Authorised.apply block

@EdgeCaseBerg
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment