I have implemented a Secured trait as described in the tutorial:
trait Secured {
...
def IsAuthenticated(f: => String => Request[AnyContent] => Result) = Security.Authenticated(username, onUnauthorized) { user =>
Action(request => f(user)(request))
}
...
}
And using it like this:
def list = IsAuthenticated { username => _ =>
...
}
Now, I have the following definition for file uploads:
def upload = Action(parse.multipartFormData) { request =>
...
}
Is it possible to combine IsAuthenticated and parse.multipartFormData so I am able to check the user during the file upload?
The version you implemented doesn't receive a BodyParser. In the tutorial there is a version that accepts BodyParsers:
def Authenticated[A](p: BodyParser[A])(f: AuthenticatedRequest[A] => Result) = {
Action(p) { request =>
request.session.get("user").flatMap(u => User.find(u)).map { user =>
f(AuthenticatedRequest(user, request))
}.getOrElse(Unauthorized)
}
}
Use this one. All the code you need is at the bottom of the page.
You may have to overload the function.
def IsAuthenticated[A](p: BodyParser[A])(f: => String => Request[A] => Result): Action[A] ={
...
}
def IsAuthenticated[AnyContent](f: => String => Request[AnyContent] => Result): Action[AnyContent] =
IsAuthenticated(BodyParsers.parse.anyContent)(f)
I've done something similar in my application.
Related
I currently have the following signature for my method call:
val article:Future[Option[Article]] = articleService.getById(1)
My controller looks like:
def show(id: Int) = Action.async { request =>
articleService.getById(id).onSuccess {
case articleOpt => {
articleOpt.map{
article => Ok("....")
}
}
}
Ok("fail")
}
Is there a cleaner way of handling a Future[Option[Model]] ?
def show(id: Int) = Action.async { request =>
articleService.getById(id).map {
case Some(article) => Ok(article)
case None => Ok("...")
}.recover{case ex => Ok("fail")}
}
Even more cleaner way requires using smth like scalaz's ReaderT
You can also use scalaz monad transformer OptionT:
import scalaz.OptionT
def show(id: Int) = Action.async {
OptionT(articleService.getById(id))
.map(a => Ok(s"found: ${a.name}"))
.getOrElse(NotFound("Not found"))
}
Maybe something like this
def show(id: Int) = Action.async { request =>
articleService.getById(id) map { op =>
op match {
case Some(acle) => Ok(s"found: ${acle.name}")
case None => NotFound("Not found")
}}
}
I'm trying to implement an authentication mechanism similar to this example:
def HasToken(action: String => EssentialAction): EssentialAction = EssentialAction { requestHeader =>
val maybeToken = requestHeader.headers.get("X-SECRET-TOKEN")
maybeToken map { token =>
action(token)(requestHeader) // apply requestHeader to EssentialAction produces the Iteratee[Array[Byte], SimpleResult]
} getOrElse {
Done(Unauthorized("401 No Security Token\n")) // 'Done' means the Iteratee has completed its computations
}
}
However, in my case I'm mapping a random token value to a session on the server side stored in Mongodb. The goal was to be able to let a user terminate all his other sessions at will.
However, the data I get from ReactiveMongo is going to be wrapped in a Future.
I would like something like this:
def HasToken(action: String => EssentialAction): EssentialAction = EssentialAction { requestHeader =>
val maybeToken = requestHeader.headers.get("session")
maybeToken map { token =>
//This returns a future..
Session.find(session).map { result =>
result match
case Some(session) => action(session)(requestHeader)
case None => Done(Unauthorized())
}
} getOrElse {
Done(Unauthorized("401 No Security Token\n")) // 'Done' means the Iteratee has completed its computations
}
}
Is this possible with EssentialAction?
Iteratee.flatten goes from Future[Iteratee[A, E]] => Iteratee[A, E] so you could do it like this:
def HasToken(action: String => EssentialAction): EssentialAction = EssentialAction { requestHeader =>
val maybeToken = requestHeader.headers.get("session")
val futureIteratee: Future[Iteratee[Array[Byte], SimpleResult]] = maybeToken map { token =>
//This returns a future..
Session.find(token).map {
case Some(session) => action(session)(requestHeader)
case None => Done[Array[Byte], SimpleResult](Unauthorized("Invalid token"))
}
} getOrElse {
Future.successful(Done[Array[Byte], SimpleResult](Unauthorized("401 No Security Token\n")))
}
Iteratee.flatten(futureIteratee)
}
You can use an ActionBuilder as the invokeBlock method return a Future[SimpleResult] so you can flatMap your future into a call to the underlying block
Something like
object Authenticated extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
Session.find(session).map { result =>
result match
case Some(session) => block(new AuthenticatedRequest(session))
case None => Unauthorized()
}
}
}
where AuthenticatedRequest is your request type that wraps your session object
I'm trying to implement authentication in my Play 2.2.1 app, and I can't quite figure out how to make it work with an action that returns a Future[Result].
This post describes pretty close to what I'm trying to do, except without returning Future[Result]:
Play 2.0 Framework, using a BodyParser with an authenticated request
How can I get it to work with Futures? I.e. how would I implement this function:
def IsAuthenticated(f: => String => Request[Any] => Future[Result])
or, better yet, this function:
def IsAuthenticated[A}(b:BodyParser[A])(f: => String => Request[Any] => Future[Result])
which would feed into this function:
def AuthenticatedUser(g: Account => Request[AnyContent] => SimpleResult) = IsAuthenticated {...}
to wrap asynchronous actions in my controllers?
This part I can do:
def IsAuthenticated(f: => String => Request[AnyContent] => Future[SimpleResult]) = {
Security.Authenticated(email, onUnauthorized) {
user => Action.async(request => f(user)(request))
}
}
But if I try to use IsAuthenticated in my wrapper function:
def AuthenticatedUser(g: Account => Request[AnyContent] => Future[SimpleResult]) = IsAuthenticated {
email => implicit request => Account.find(email).map {
opt => opt match {
case Some(account) => g(account)(request)
case None => Future(onUnauthorized(request))
}
}
}
(Account.find returns a Future[Option[Account]] 'cause it's a mongodb call that may take some time. The desire to do the future thing right is what's causing me so much grief now)
I can't get AuthenticatedUser to satisfy the compiler. It says it's getting a Future[Future[SimpleResult]] instead of a Future[SimpleResult].
So, how best to build this whole thing? I need to be able to make authentication wrappers that rely on db calls that are asynchronous.
I'm sure I'm just dense and missing something obvious...
EDIT: Here's what I ended up with. Thank you Jean for pointing me in the right direction.
I found AuthenticatedController while rooting around and it's pretty close to what I'm looking for. I wanted two types of authentication: User (authenticated user) and Administrator (to wrap code for admin tasks).
package controllers
import models.Account
import play.api.mvc._
import scala.concurrent.Future
trait Secured {
class AuthenticatedRequest[A](val account: Account, request: Request[A]) extends WrappedRequest[A](request)
object User extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
request.session.get("email") match {
case Some(email) => {
Account.find(email).flatMap {
case Some(account) => {
block(new AuthenticatedRequest(account, request))
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
}
object Administrator extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
request.session.get("email") match {
case Some(email) => {
Account.find(email).flatMap {
case Some(account) => if (account.admin) {
block(new AuthenticatedRequest(account, request))
} else {
Future(Results.Redirect(routes.Index.index()))
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
}
}
There have been changes in play 2.2 to make it easier to compose actions. The resource you are referring to is outdated.
Instead you should create a custom action builder by extending ActionBuilder to create your action, this will get you all the fancy apply methods you may need (including async support and all)
For example you may do :
trait MyAction extends Results{
class MyActionBuilder[A] extends ActionBuilder[({ type R[A] = Request[A] })#R] {
def invokeBlock[A](request: Request[A],
block: Request[A] => Future[SimpleResult]) ={
// your authentication code goes here :
request.cookies.get("loggedIn").map { _=>
block(request)
} getOrElse Future.successful(Unauthorized)
}
}
object MyAction extends MyActionBuilder
}
which you can then use as such :
object MyController extends Controller with MyAction{
def authenticatedAction=MyAction {
Ok
}
def asyncAuthenticatedAction=MyAction.async {
Future.successful(Ok)
}
def authenticatedActionWithBodyParser = MyAction(parse.json){ request =>
Ok(request.body)
}
}
For brevity's sake I used a very trivial authentication mechanism you will want to change that :)
Additionally, you can create a custom "request" type to provide additional information. For instance you could define a AuthenticatedRequest as such :
case class AuthenticatedRequest[A](user: User, request: Request[A]) extends WrappedRequest(request)
Provided you have a way to get your user such as
object User{
def find(s:String): Option[User] = ???
}
Then change your builder definition a bit as such
class MyActionBuilder[A] extends
ActionBuilder[({ type R[A] = AuthenticatedRequest[A] })#R] {
def invokeBlock[A](request: Request[A],
block: AuthenticatedRequest[A] => Future[SimpleResult]) ={
// your authentication code goes here :
(for{
userId <- request.cookies.get("userId")
user <- User.find(userId.value)
}yield {
block(AuthenticatedRequest(user,request))
}) getOrElse Future.successful(Unauthorized)
}
}
Your controller now has access to your user in authenticatedActions:
object MyController extends Controller with MyAction{
val logger = Logger("application.controllers.MyController")
def authenticatedAction=MyAction { authenticatedRequest =>
val user = authenticatedRequest.user
logger.info(s"User(${user.id} is accessing the authenticatedAction")
Ok(user.id)
}
def asyncAuthenticatedAction = MyAction.async { authenticatedRequest=>
Future.successful(Ok(authenticatedRequest.user.id))
}
def authenticatedActionWithBodyParser = MyAction(parse.json){ authenticatedRequest =>
Ok(authenticatedRequest.body)
}
}
This is the action composition taken from a sample that comes with the Play Framework
def IsAuthenticated(f: => String => Request[AnyContent] => Result) = Security.Authenticated(username, onUnauthorized) { user =>
Action(request => f(user)(request))
}
So, Security.Authenticated takes an username: RequestHeader => Option[String] and onAuthorized: RequestHeader=>SimpleResultand the second group of parantheses take String => Action[A]
And then in the controller I have:
def index = isAuthenticated { ...code }}
The code above is this, so I assume this is the f function => String => Request[AnyContent] => Result. Now, what I don't understand is what really happens here. I am not talking about User.findByEmail...., I'm talking about username => _ => .... What would the signature of this function look like if I called it directly?
username => _ =>
User.findByEmail(username).map { user =>
Ok(
html.dashboard(
Project.findInvolving(username),
Task.findTodoInvolving(username),
user
)
)
}.getOrElse(Forbidden)
If there was def isAuthenticated(f: => Request[AnyContent] => Result) I'd know how to use it and I'd understand it. But the extra "data" is confusing me.
UPDATE:
I guess I found something:
def f2: String => Int => List[Char] = x => _ => x.toList
And this would be called as:
f2("Andrew")(2) //there can be anything replacing 2 because I don't have access to it anyway
So the function above that I asked primarily about would be:
def f: => String => Request[AnyContent] => Result = username => _ => User.find.....
Am I right?
I get a "No by name parameter allowed here error".
If they don't use the second parameter why are they using String => Request => Result and not just simply String => Result?
That function definition is actually a curried function definition.
String => Request => Result actually means: f(s:String):(r:Request)=>Result ie a function that takes a String and returns a function that takes a Request and returns a Result.
Check out the part "Spicing up your functions": http://danielwestheide.com/blog/2013/01/30/the-neophytes-guide-to-scala-part-11-currying-and-partially-applied-functions.html
For me, the examples at https://github.com/mariussoutier/PlayBasics/tree/master/play-2.2-migration are very enlightening.
In the zentasks example for Play2 we have the method
def isAuthenticated(f: => String => Request[AnyContent] => Result) = {
Security.Authenticated(username, onUnauthorized) { user =>
Action(request => f(user)(request))
}
}
What I would like to do is add another method that I could use if I wanted to get the user directly from the database.
It gets a little boring having to add a wrapper in all methods
def method() = isAuthenticated { username => implicit request =>
UserDAO.findOneByEmail(username).map { user =>
Ok(html.user.view(user))
}.getOrElse(Forbidden)
}
I'm new to functional programming and all these => is making my head spin :)
Any suggestions?
You can define another method, for example IsAuthenticatedUser, which would take a parameter of type User => Request[AnyContent] => Result:
def IsAuthenticatedUser(f: User => Request[AnyContent] => Result) = IsAuthenticated { email => request =>
UserDAO.findOneByEmail(email).map { user =>
f(user)(request)
}.getOrElse(Forbidden)
}
You can then use it like as follows:
def method = IsAuthenticatedUser { user => request =>
Ok(html.user.view(user))
}
This was the final solution
trait Secured {
def username(request: RequestHeader) = request.session.get(Security.username)
def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Application.login)
def withAuth(f: => String => Request[AnyContent] => Result) = {
Security.Authenticated(username, onUnauthorized) { user =>
Action(request => f(user)(request))
}
}
def withUser(f: User => Request[AnyContent] => Result) = withAuth { username => implicit request =>
UserDAO.findOneByUsername(username).map { user =>
f(user)(request)
}.getOrElse(onUnauthorized(request))
}
}
Usage:
object Application extends Controller with Secured {
def index = withAuth { username => implicit request =>
Ok(html.index(username))
}
def user() = withUser { user => implicit request =>
val username = user.username
Ok(html.user(user))
}
}
As you can see, if the session doesn't exist, the user gets redirected to the login page as specified in the trait.
And same goes if the username can't be found in the DAO.
Semi-solution is to encapsulate fetching user data steps into some function and calling it from template level (not on each action), as every template is just a scala function.
Thanks to this approach if you have several actions using the same view (or even layout) you don't have to fetch logged user everytime ie:
in the user view:
#defining(Application.getLoggedUser){ user =>
#yourlayout("Welcome") {
<h2>Hello #user.name</h2>
...
}
}