I am building a security trait using AuthenticatedBuilder:
trait Security { self: Controller =>
object AuthenticatedAction extends AuthenticatedBuilder
(request => getUserFromRequest(request))
}
I have an implementation in that trait for this function:
def getUserFromRequest(request: RequestHeader) = {
// ...whatever...
}
This function looks for an authentication token in the request, validates it and maps it to a user id, which is then used to find a user in the database. If all is well, a User instance is returned, otherwise None. It's part of a wider authentication scheme, based pretty much on the play-angular-seed github project. Duplicating all of that code here is redundant, suffice to say this method either returns a User or None to signify authentication.
This lets me write a controller like this:
object Accounts extends Controller with Security {
def list = AuthenticatedAction { implicit request =>
// I can use request.user here to implement authorisation as needed
Ok(toJson(Account.list))
}
}
In this Controller, because of AuthenticatedAction I am guaranteed to have a User instance available via the request.user value. If there is no authorized user, the code block itself does not get executed and returns a Forbidden result.
This is all fine and works really well.
I have one use-case where I need to do all this authentication, but do some custom handling of the unauthorised case - i.e. I want to execute my own code block in this Controller when authorisation fails.
I know that AuthenticatedBuilder can accept such a function during construction:
def apply[U](userinfo: (RequestHeader) ⇒ Option[U],
onUnauthorized: (RequestHeader) ⇒ Result = ...): AuthenticatedBuilder[U]
I'm new to Scala, and I can't figure out the syntax/changes required to pass such a function from my Controller via my AuthenticatedAction object to the AuthenticatedBuilder. Ideally I would pass an optional onUnauthorized parameter, or I could have a separate AuthenticatedAction implementation I suppose.
How can it be done?
Objects cannot have parameter lists, so you need to use a method and create the action on the fly.
def Authenticated(unauth: RequestHeader => Result) =
new AuthenticatedBuilder(getUserFromRequest, unauth)
Related
I'm confused as to how the request object can be injected into the Action.
Was hoping someone could create a simple prototype of the following in scala:
class HomeController() extends AbstractController {
def index() = Action { request =>
Ok("hello")
}
}
What I mean is, create the above classes/functions to simple return a string "hello", with the ability to get other objects in scope like "request".
abstract class AbstractController()
case class Action(???)
case class Ok(????)
I am just confused has to how you can create an Action {} and then have request available in the block specifically.
If you write Action { request => ??? }, you're calling the apply method in the Action companion object. This method takes one parameter which is a function that takes a request and returns a response. The request value is the parameter of the function that you pass to the apply method.
Here's the method that you're calling.
If you would write a class like Action yourself, it may look similar to this:
case class Action(f: Request => Ok)
Combining an ActionBuilder that transforms a request into a custom WrappedRequest with an additional type parameter, and then combining that with an ActionFilter causes the type of the custom WrappedRequest to be dropped.
Why is this and is there a fix?
For example, lets say I need to an authentication ActionBuilder and an optional, authorisation ActionFilter where the user type we need can vary depending on use.
See this contrived code:
case class AuthRequest[A, U](val request: Request[A], val user: U) extends WrappedRequest[A](request)
case class TestUser(id: String)
case class AuthenticationAction[A]() extends ActionBuilder[({type λ[B] = AuthRequest[B, TestUser]})#λ] {
override def invokeBlock[A](request: Request[A], block: (AuthRequest[A, TestUser]) => Future[Result]): Future[Result] =
block(AuthRequest(request, TestUser("test-id")))
}
case class AuthorisedAction[A]() extends ActionFilter[({type λ[B] = AuthRequest[B, TestUser]})#λ] {
// does some additional authorisation checks for added security
def authorised(user: TestUser) = true
override def filter[A](request: AuthRequest[A, TestUser]) = Future.successful {
if( authorised(request.user) ) None
else Some(Unauthorized)
}
}
Please note: The type function in the above is required as per this answer because of the additional type parameter on the AuthRequest. Again this is required because this API will be used with more than one type of user.
If I then implement a controller that uses the above:
class ExampleController extends Controller {
def secured = (AuthenticationAction() andThen AuthorisedAction()) { request =>
Ok(request.user.id)
}
}
I get a compiler error (value user is not a member of play.api.mvc.WrappedRequest[play.api.mvc.AnyContent]). In other words, the type of the variable request above is WrappedRequest instead of the expected type of AuthRequest[play.api.mvc.AnyContent, TestUser]!
I understand that most use cases the AuthenticationAction and AuthorisedAction would be combined into a single action but, because authorisation is optional, I would like to keep these as separate concerns.
Is this possible? Is this a bug in the Play Framework API? Or a Scala bug? Or human error?
Using your example I was able to compose the actions like this:
class ExampleController extends Controller {
def secured = AuthorisedAction().compose(AuthenticationAction()) { request =>
Ok(request.user.id)
}
}
It's interesting to note that in both cases IntelliJ's type inspection sees request as being of type AuthRequest[AnyContent, TestUser] -- it's only scalac that sees it as a WrappedRequest.
I've created a custom Action that prevents unauthorized users from accessing protected functionality:
class SecureAction extends ActionBuilder[SecureRequest] {
def invokeBlock[A](request: Request[A], block: SecureRequest[A] => Future[Result]) = {
...
future.flatMap {
case token if (!isAuthorized(token)) =>
Logger.info(s"request ${request.path} not authorized: user ${token.username} does not have required privileges")
Future.successful(Unauthorized(error(requestNotAuthorized))))
case ...
}
}
}
If the current user is not authorized, then SecureAction returns Unauthorized and never executes the provided action code. Below is how my Controller looks like:
object MyController extends Controller {
...
def saveFile = SecureAction.async(fsBodyParser) { implicit request =>
// code here not executed if current user has not required privileges
...
}
}
The problem is that even if the current user is not authorized and SecureAction returns Unauthorized without executing the action code, the body parser gets still invoked... and this is not what I was expecting.
That said, the question is: how do I prevent the body parser (i.e fsBodyParser) from being invoked in case SecureAction returns Unauthorized?
Take a look at EssentialAction (https://www.playframework.com/documentation/2.2.x/api/scala/index.html#play.api.mvc.EssentialAction)
As you can see, this is the definition of EssentialAction:
trait EssentialAction extends (RequestHeader) ⇒ Iteratee[Array[Byte], SimpleResult] with Handler
So, if you want to operate at the request header level, prefer EssentialActions. Unlike Action/ActionBuilders they don't need interaction with BodyParsers.
It's worth mentioning this awesome post by #marius-soutier: http://mariussoutier.com/blog/2013/09/17/playframework-2-2-action-building-action-composition/
Background
I am trying to understand best practices for bringing implicit objects into scope within a Scala application.
I have a Playframework 2.2.0 (Scala 2.10) web app that mixes in a trait for Authorization. It checks. The Authenticated object checks that there is a user_id in scope, attempts to retrieve the user info, access token, and a data package object called a MagicNotebook from cache, database, and web service call. If the request is valid, then various objects are added to the wrapped request.
object Authenticated extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A],
block: (AuthenticatedRequest[A] => Future[SimpleResult])) = {
request.session.get(userName).map { implicit userId =>
Cache.getAs[DbUser](userKey).map { user =>
Cache.getAs[String](accessTokenKey).map { accessToken =>
Cache.getAs[MagicNotebook](magicNotebookKey(userId)).map { notebook =>
block(AuthenticatedRequest(user, accessToken, notebook, request) )
}.getOrElse(startOver)
}.getOrElse {
requestNewAccessToken(user.token).flatMap { response =>
persistAccessToken(response).map { accessToken =>
Cache.getAs[MagicNotebook](magicNotebookKey(userId)).map { notebook =>
block(AuthenticatedRequest(user, accessToken, notebook, request))
}.getOrElse(startOver)
}.getOrElse(startOver)
}
}
}.getOrElse(startOver) // user not found in Cache
}.getOrElse(startOver) // userName not found in session
}
}
}
case class AuthenticatedRequest[A](user: DbUser,
accessToken: String,
magic: MagicNotebook,
request: Request[A])
extends WrappedRequest[A](request)
Question
What is the best way to bring these implicit variables into scope?
Through an implicit class?
I tried to use an implicit companion class, with the following code:
object Helper {
implicit class Magical(request: AuthenticatedRequest[AnyContent]) {
def folderMap = request.magic.fMap
def documentMap = request.magic.dMap
}
}
However, I don't really get the benefit of an implicit this way:
def testing = Authenticated { implicit request =>
import services.Helper._
request.magic.home.folders // doesn't compile
request.magic.home.folders(Magical(request).ffMap) // compiles, but not implicitly
Ok("testing 123")
}
Through an import statement?
One possibility I considered was through an import statement within the controller. Here, the request has a MagicNotebook object in scope that I would like to use as an implicit variable.
def testing = Authenticated { implicit request =>
import request.magic._
request.magic.home.folders // implicit map is a parameter to the `folder` method
Ok("testing 123")
}
Through a companion trait?
Here, I create a companion trait that is mixed into the Authenticate trait that includes the two maps of the MagicNotebook object into scope of the controller.
trait Magic {
implicit def folderMap[A](implicit request: AuthenticatedRequest[A]) =
request.magic.fMap
implicit def docMap[A](implicit request: AuthenticatedRequest[A]) =
request.magic.dMap
}
My preference is the companion trait solution, but I was wondering if there might be a better way that I overlooked. I ended up re-writing methods that use the implicit variable, to use the MagicNotebook's two maps instead of whole object as implicit parameters.
But again, I was wondering if there might be a better way.
I am quite partial to package objects for this sort of thing. See What's New in Scala 2.8: Package Objects for a description. Package objects effectively allow you to put implicit classes into a package, which you can't otherwise do.
However, the main snag with this approach is that you can't split the definition of an object across multiple source files, so because the implicit classes need to be defined within the package object, they also need to be all in the same source file. If you have many implicit classes you wish to have imported, this can result in a large and unwieldy source file. However, that in itself is a sign that you have a “package god object” which should be split.
One of the ways I know of defining implicits is by using Package Objects.
package implicitIdiomatic {
implicit def nullableLongToOption(l:java.lang.Long) = Option(l)
}
}
package implicitIdiomatic
class ImplicitIdiomaticTest{
val l:Long = 1
longOpt(l)
def longOpt(l:Option[Long]) = l match {case Some(l1) => println(l1); case None => println("No long")}
}
Kind of useless example but hope you get the idea. Now when longOpt gets l, it is converted to Option[Long] using the implicit.
As long as you are working in the same package as defined by the package object you don't need the import statement.
I have a Social object, responsable of connecting to Twitter, facebook, etc, and retrieve provider info for the specified user
For each provider I implemented a singleton TwitterAdapter, all inheriting from an abstract class SocialAdapter
here's the code: https://github.com/RestOpenGov/ideas-ba/blob/master/webservice/app/services/security/SocialConnector.scala#L98
For testing, I would obviously like to mock the TwitterAdapter, so that instead of connecting with twitter it returns some fixed response.
One solution I've found was to inject the list of adapters using an implicit parameter. The problem with this solution is that the Social.retrieveSocialProviderInfo is called from other functions, so I have to pass around the implicit List[SocialAdapter] parameter though all the call chain, like this:
def createApplicationToken(accessToken: AccessToken)
(implicit adapters: List[SocialAdapter] = Social.defaultAdapters)
: Either[List[Error], ApplicationToken] = {
// go to social info provider and fetch information
retrieveProviderInfo(accessToken).fold(
[...]
def retrieveProviderInfo(accessToken: AccessToken)
(implicit adapters: List[SocialAdapter] = Social.defaultAdapters)
: Either[List[Error], IdentityProviderInfo] = {
[...]
and finally
object Social {
val defaultAdapters = List(TwitterAdapter, FacebookAdapter)
def retrieveSocialProviderInfo
(accessToken: AccessToken)
(implicit adapters: List[SocialAdapter] = Social.defaultAdapters) // adapters can be injected
: Option[IdentityProviderInfo] = {
[...]
You get the idea
It works fine, normally I just ignore the second group of parameters and pick the default from Social.defaultAdapters, I only set it to List(MockTwitterAdapter, MockFacebookAdapter) when testing, but I'm cluttering the code just to be able to test it.
The other solution would be to make Social.defaultAdapters a var (instead of a val) and just change it for testing, normally in production mode it would always have the same value.
I think this must be a pretty common scenario. Is there a better strategy to handle these situations? Or maybe some way to extend the scope of the implicit assignment? Or shall I just go with a full-featured dependency injection framework?
A simple approach can be to just use traits all along:
// you can test this trait and override the adapters as you wish
// by overriding the defaultAdapters member
trait Social {
implicit val defaultAdapters = List(TwitterAdapter, FacebookAdapter)
def retrieveSocialProviderInfo(accessToken: AccessToken):
Option[IdentityProviderInfo] = ...
}
// you can use this object directly in your production code
// if you don't want to mix it in
object Social extends Social
// or use the trait by mixing it with another
trait Application extends Social {
def createApplicationToken(accessToken: AccessToken):
Either[List[Error], ApplicationToken] = {
// the defaultAdapters are accessible to the
// retrieveProviderInfo method
retrieveProviderInfo(accessToken).fold(...)
}