How to pass a parameter from View to snippet in Lift? - lift

I will use slightly modified Listing 4.7 from the book Exploring Lift to ask my question.
// In Boot.boot:
LiftRules.viewDispatch.append {
case List("Expenses", "recent", acctId, authToken) =>
Left(() => Full(RSSView.recent(acctId, authToken)))
// This is a dispatch via the same LiftView object. The path
// "/Site/news" will match this dispatch because of our dispatch
// method defined in RSSView. The path "/Site/stuff/news" will not
// match because the dispatch will be attempted on List("Site","stuff")
case List("Site") => Right(RSSView)
}
// Define the View object:
object RSSView extends LiftView {
def dispatch = {
case "news" => siteNews
}
def recent(acctId : String, authToken : String)() : NodeSeq = {
// User auth, account retrieval here
...
<lift:surround with="rss" at="content">
<lift:Vote.image />
</lift:surround>
}
// Display a general RSS feed for the entire site
def siteNews() : NodeSeq = { ... }
}
How do I pass acctId from the view function recent into the snippet lift:Vote.image? Thanks.

If you're attempting to get the acctId and authToekn from boot for a user, this won't work. Boot only runs when the web application starts up, not once for every user.
You'll have to set SessionVar's when the user logs in, or when you detect the autologin cookie, and then access the sessionVar where you need it.

Related

Spray request - Complete request only after nested futures

New to spray and scala. Been struggling to get it right for couple of days now.
I am trying to merge facebook oauth2 login + user login details into the database in case the same user logs in by different ways(user/pass or fb login).
Pasting below spray routing snippet.
path("facebook") {
post{
entity(as[JObject]) { json =>
val fb: FacebookAuthModel = json.extract[FacebookAuthModel]
complete {
//Get user details from fb oauth2
val fbUser = fbAuth.getIdentity(fb) match {
case Right(user: User) => user
case Left(error: Failure) => throw new FailureException(error)
}
//Check if user is already present either by fb id or email
val userFuture = userRepo(FetchUserByFacebook(fbUser.facebook.get,fbUser.email))
userFuture.map {
case u: User => {
//user present but fb id not attached yet
if (u.facebook.isEmpty) {
//update fb id for the user - fire to actor and forget, i.e no callback to sender
userRepo(UpdateFacebookId(u.id.get, fbUser.facebook.get))
}
//complete request with a token - request(1)
AuthToken(token=jwt.createToken(u))
}
case None => {
//first time user using fb login
userRepo(CreateUser(fbUser)).map {
//complete request with the token - request(2)
case createdUser: User => AuthToken(token=jwt.createToken(createdUser))
case None => throw new FailureException(Failure("Not able to CreateUser", FailureType.Unauthorized))
}
}
}
}
}
}
}
Everything works fine except in case of first time user using fb login (refer request(2)).Request gets completed with empty response before the nest future could complete.
I tried flatMapping the result from userFuture and then using onComplete on it to give the appropriate response, but it din't work.
Any idea how I could successfully complete the request(request(2)) with the token?
If one of the two branches in your code execution path could result in a Future, then you have to code to this as the lowest common denominator when dealing with userFuture. That means flatMap on userFuture and using Future.successful in the case where you don't have an explicit second Future to deal with. Something along this line:
def handleUserResult(a:Any):Future[AuthToken] = a match{
case u:User =>
if (u.facebook.isEmpty) {
userRepo(UpdateFacebookId(u.id.get, fbUser.facebook.get))
}
Future.successful(AuthToken(token=jwt.createToken(u)))
case None =>
userRepo(CreateUser(fbUser)).map {
case createdUser: User =>
AuthToken(token=jwt.createToken(createdUser))
case None =>
throw new FailureException(Failure("Not able to CreateUser", FailureType.Unauthorized))
}
}
Once you define that method, you can use it on userResult as follows:
userResult.flatMap(handleUserResult)
I didn't check this code for compilation issues. I was more trying to show the general approach of flatMap used to handle two cases, one that produces another second Future and one that does not.

Token based authentication in Play filter & passing objects along

I've written an API based on Play with Scala and I'm quite happy with the results. I'm in a stage where I'm looking at optimising and refactoring the code for the next version of the API and I had a few questions, the most pressing of which is authentication and the way I manage authentication.
The product I've written deals with businesses, so exposing Username + Password with each request, or maintaining sessions on the server side weren't the best options. So here's how authentication works for my application:
User authenticates with username/password.
Server returns a token associated with the user (stored as a column in the user table)
Each request made to the server from that point must contain a token.
Token is changed when a user logs out, and also periodically.
Now, my implementation of this is quite straightforward – I have several forms for all the API endpoints, each one of which expects a token against which it looks up the user and then evaluates if the user is allowed to make the change in the request, or get the data. So each of the forms in the authenticated realm are forms that need a token, and then several other parameters depending on the API endpoint.
What this causes is repetition. Each one of the forms that I'm using has to have a verification part based on the token. And its obviously not the briefest way to go about it. I keep needing to replicate the same kind of code over and over again.
I've been reading up about Play filters and have a few questions:
Is token based authentication using Play filters a good idea?
Can a filter not be applied for a certain request path?
If I look up a user based on the supplied token in a filter, can the looked up user object be passed on to the action so that we don't end up repeating the lookup for that request? (see example of how I'm approaching this situation.)
case class ItemDelete(usrToken: String, id: Long) {
var usr: User = null
var item: Item = null
}
val itemDeleteForm = Form(
mapping(
"token" -> nonEmptyText,
"id" -> longNumber
) (ItemDelete.apply)(ItemDelete.unapply)
verifying("unauthorized",
del => {
del.usr = User.authenticateByToken(del.usrToken)
del.usr match {
case null => false
case _ => true
}
}
)
verifying("no such item",
del => {
if (del.usr == null) false
Item.find.where
.eq("id", del.id)
.eq("companyId", del.usr.companyId) // reusing the 'usr' object, avoiding multiple db lookups
.findList.toList match {
case Nil => false
case List(item, _*) => {
del.item = item
true
}
}
}
)
)
Take a look at Action Composition, it allows you to inspect and transform a request on an action. If you use a Play Filter then it will be run on EVERY request made.
For example you can make a TokenAction which inspects the request and if a token has been found then refine the request to include the information based on the token, for example the user. And if no token has been found then return another result, like Unauthorized, Redirect or Forbidden.
I made a SessionRequest which has a user property with the optionally logged in user, it first looks up an existing session from the database and then takes the attached user and passes it to the request
A filter (WithUser) will then intercept the SessionRequest, if no user is available then redirect the user to the login page
// Request which optionally has a user
class SessionRequest[A](val user: Option[User], request: Request[A]) extends WrappedRequest[A](request)
object SessionAction extends ActionBuilder[SessionRequest] with ActionTransformer[Request, SessionRequest] {
def transform[A](request: Request[A]): Future[SessionRequest[A]] = Future.successful {
val optionalJsonRequest: Option[Request[AnyContent]] = request match {
case r: Request[AnyContent] => Some(r)
case _ => None
}
val result = {
// Check if token is in JSON request
for {
jsonRequest <- optionalJsonRequest
json <- jsonRequest.body.asJson
sessionToken <- (json \ "auth" \ "session").asOpt[String]
session <- SessionRepository.findByToken(sessionToken)
} yield session
} orElse {
// Else check if the token is in a cookie
for {
cookie <- request.cookies.get("sessionid")
sessionToken = cookie.value
session <- SessionRepository.findByToken(sessionToken)
} yield session
} orElse {
// Else check if its added in the header
for {
header <- request.headers.get("sessionid")
session <- SessionRepository.findByToken(header)
} yield session
}
result.map(x => new SessionRequest(x.user, request)).getOrElse(new SessionRequest(None, request))
}
}
// Redirect the request if there is no user attached to the request
object WithUser extends ActionFilter[SessionRequest] {
def filter[A](request: SessionRequest[A]): Future[Option[Result]] = Future.successful {
request.user.map(x => None).getOrElse(Some(Redirect("http://website/loginpage")))
}
}
You can then use it on a action
def index = (SessionAction andThen WithUser) { request =>
val user = request.user
Ok("Hello " + user.name)
}
I hope this will give you an idea on how to use Action Composition
The people at Stormpath has a sample Play application providing authentication via their Backend Service. Some of its code could be useful to you.
It uses username/password rather than tokens, but it should not be complex to modify that.
They have followed this Play Document:
https://www.playframework.com/documentation/2.0.8/ScalaSecurity.
The specific implementation for this is here:
https://github.com/stormpath/stormpath-play-sample/blob/dev/app/controllers/MainController.scala.
This Controller handles authentication operations and provides the isAuthenticated action via the Secured Trait (relying on play.api.mvc.Security). This operation checks if the user is
authenticated and redirects him to the login screen if he is not:
/**
* Action for authenticated users.
*/
def IsAuthenticated(f: => String => Request[AnyContent] => Future[SimpleResult]) =
Security.Authenticated(email, onUnauthorized) { user =>
Action.async { request =>
email(request).map { login =>
f(login)(request)
}.getOrElse(Future.successful(onUnauthorized(request)))
}
}
Then, each controller that needs authenticated operations must use the
Secured Trait:
object MyController extends Controller with Secured
And those operations are "wrapped" with the IsAuthenticated action:
def deleteItem(key: String) = IsAuthenticated { username => implicit request =>
val future = Future {
MyModel.deleteItem(request.session.get("id").get, key)
Ok
}
future.map(
status => status
)
}
Note that the deleteItem operation does not need a username, only the key. However, the authentication information is automatically obtained from the session. So, the business' API does not get polluted with security-specific parameters.
BTW, that application seems to have never been officially released so consider this code a proof of concept.

Play 2.2 for Scala: is it possible to modify the session of an incoming request?

I'm trying to use action composition to add a fake user to Session.
def GuestAction(f: Request[AnyContent] => Result): Action[AnyContent] = {
Action { request =>
var myUser = searchUser(request.session)
if ( myUser == null ) {
myUser = newUser()
}
f(request).withSession("user" -> myUser)
}
}
In my controller there is
def action1 = GuestAction { implicit request =>
// My code
Ok()
}
def action2 = GuestAction { implicit request =>
val user = request.session.get("user").get
// My code
Ok()
}
When I open Chrome and browse to the route pointing to "action1" and then to the route pointing to "action2", everything works fine: I got a new user and it is attached to the session.
On the contrary, when I open Chrome and I browse to the route pointing to "action2" first, I got an error because my "request.session" is empty, and that's obvious: using .withSession() the session is attached to the Result, not to the incoming request.
So, in order to make this work, I need to attach the session key/value pairs to the incoming request - like it is possible in FakeRequest.withSession(), but there's no such method in Request.
What would you suggest in order to fix this issue?
You're very nearly there - but to get the maximum value from your Action composition, you should be able to make your "client" Actions totally unaware of the session - they really only care about the User object after all. You want to be able to write:
def action2 = GuestAction { implicit user => implicit request =>
// Do something with 'user', whether it's a guest or real
println(s"My user is $user")
Ok()
}
action2 doesn't care how the User was obtained, but it can rely on one being available. To make this happen, GuestAction needs to be something like this:
def GuestAction(f: (User) => Request[AnyContent] => Result): Action[AnyContent] = {
Action { request =>
var myUser = searchUser(request.session)
if ( myUser == null ) {
myUser = newUser()
}
f(myUser)(request)
}
}
The only remaining piece of the puzzle is putting a user into the session - which you can still do with a conventional response.withSession() as part of a successful login process.

Scala Play framework 2.2 - How to get information about a users loginstatus in template

I have the following use-case. I implemented a very simple authentication in my play app which adds a session cookie if a user logs in (See code below).
This code works fine so far. What I want to achieve now is to check in my main template if a user is logged in or not and display login/logout elements on the page according to the user status.
How can I achieve this in the most elegant way?
I have found sources where people access the session variables directly from the template with play <= 2.1. It seems like this method doesn't work for 2.2 anymore and is deprecated?
Do I have to pass a boolean value in every action to the template to define if a user is logged in??
Wrapper Action
case class Authenticated[A](action: Action[A]) extends Action[A] {
def apply(request: Request[A]): Future[SimpleResult] = {
if (request.session.get("user").getOrElse("").equals("user")) {
action(request)
} else {
Future.successful(Redirect("/login").withSession(("returnUrl", request.path)))
}
}
lazy val parser = action.parser
}
Submit Part of Login Controller
def submit = Action { implicit request =>
loginForm.bindFromRequest.fold(
errors => Ok(html.login.form(errors)),
requestUser => {
val user: String = Play.current.configuration.getString("fillable.user").getOrElse("")
val password: String = Play.current.configuration.getString("fillable.password").getOrElse("")
if (requestUser.name.equals(user) && requestUser.pw.equals(password))
Redirect(request.session.get("returnUrl").getOrElse("/")).withSession(session + ("user" -> requestUser.name) - "returnUrl")
else
Ok(html.login.form(loginForm, "error", Messages("error.wrongCredentials")))
})
}
Example Controller Action where Authentication is needed
def submit = Authenticated {
Action.async { implicit request =>
...
}
}
So what I found out now is that if the Controller Action uses an implicit request(like the one in my question above) I can use that request and therefore the session in my template if I add this to the head of the template:
(implicit request: Request[Any])
I am not sure if this is a good approach so I am happy if someone can approve it.

Changing Authentication Method in Play IsAuthenticated

I'd like to create a custom authentication method for a Play2 Framework app.
I'm trying it in Scala and Play -- and I'm new to both.
In the zentask example there is there is a function called IsAuthenticated in the Trait Secured:
def IsAuthenticated(f: => String => Request[AnyContent] => Result) = Security.Authenticated(username, onUnauthorized) { user =>
Action(request => f(user)(request))
}
This definition is fairly complex. I've found a number of questions regarding the syntax of this definition on stackoverflow, but I'm still not sure how to change this.
I can see an authentication check happening in User.authenticate via a database lookup. But the authentication that I want to do doesn't use a database. I'm not sure how or where to wire in a different type of authentication. Is Security.Authenticated() wired into using the User class/object?
Security.Authenticated just checks if session contains "username" key. If it does, user supposed to be authenticated.
You should authenticate your users yourself, by doing database lookup, or any other way. Then, store user id(or email, or just name) in the session:
val user = // fetch user info
Redirect("/").withSession("userId" → user.id.toString)
Then wrap actions in Security.Authenticated call:
def someAction = Security.Authenticated(
req => req.session.get("userId"),
_ => Redirect(views.html.login())) { userId =>
Action {
Ok(html.index())
}
}
The first argument to Authenticated is a function that retrieves user id from the session. It returns an Option[String], i.e. Some[String] if there is id in the session or None if there isn't.
req => req.session.get("userId")
The second argument is a function that returns Result to use, if session isn't contains user id. You will typically want a redirect to a login page.
_ => Redirect(views.html.login())
The final argument is a function returning Action. It is used if user is authenticated.
userId => Action {
Ok(html.index())
}
You are not forced to use play implementation, feel free to wrap it in handy helper, or write it from scratch to fit your needs:
def myAuth(f: String => Result) = Security.Authenticated(
req => req.session.get("userId"),
_ => Redirect(views.html.login())) { userId =>
Action {
f(userId)
}
}
def someAction = myAuth { userId =>
Ok(html.index())
}