I have a login form which checks if the user selected "remember me" and if the condition is met, it will automatically fill in the email field.
This is my Form object:
val loginForm = Form(
tuple(
"email" -> email,
"password" -> text(minLength = 3),
"remember" -> boolean
)
)
There may be a KV pair stored in the session ("remember" -> email: String). I have a working function which returns this email as an Option[String].
This is the function which renders the login page. views.html.login is a Template that accepts a Form object and an optional message: String which is used for relaying any authentication errors.
def login = Action {
request => {
logRequest(request)
if (UserInfo.isAuth(request.session)) {
Redirect(routes.Application.index())
}
else {
val email = UserInfo.getRememberedEmail(request.session)
if (email.isDefined) {
Ok(views.html.login(loginForm.fill(email.get, "", true)))
}
else {
Ok(views.html.login(loginForm))
}
}
}
}
I think the problem occurs at Ok(views.html.login(loginForm.fill(email.get, "", true))). When checking the form for errors, this is what I found:
{"remember":["error.boolean"]}
Related
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.
I am having trouble in validating and reseting some fields based on the role of a user.
I am trying to develop a rest api with grails and my problem appears when i try to reset some fields based on the role of an user. I send a json with the desired "not allowed" changes via PUT to the controller. I modify the not allowed fields to ones that are correct for me and then call .save() and the "not alowed" fields are updated with their sent value, not with the modified by me values. Here is the code.
THE MODEL
package phonebook
class User {
String firstName
String lastName
String phoneNo
String address
String email
String password
boolean active = false
String hash
String authToken = ""
String role = "user"
static hasMany = [contacts:Contact]
static constraints = {
firstName(blank: false)
lastName(blank: false)
address(blank: true)
phoneNo(unique: true)
email(blank: false, unique: true)
password(blank: false)
role(blank: false, inList: ["user", "admin"])
hash(blank: true)
authToken(blank: true)
active(inList:[true,false])
}
}
THE METHOD FROM CONTROLLER:
#Transactional
def update(User userInstance) {
if (!isAuthenticated()){
notAllowed()
return
}
if (userInstance == null) {
notFound()
return
}
//if(isAdmin()){
def userBackup = User.findById(userInstance.id)
userInstance.role = userBackup.role
userInstance.active = userBackup.active
userInstance.hash = userBackup.hash
userInstance.authToken = userBackup.authToken
//}
if (userInstance.hasErrors()) {
respond userInstance.errors, view:'edit'
return
}
userInstance.save flush:false
request.withFormat {
'*'{ respond userInstance, [status: OK] }
}
}
THE JSON SENT VIA PUT
{
"id":"1",
"firstName": "Modified Name 23",
"role":"admin",
"active":"true",
"hash":"asdasd"
}
The above code should not modify my values for hash or active or role even if they are sent.
Any ideas?
Thanks.
The reason your changes are being saved is because by default any changes made to a domain instance will be flushed at the end of the session. This is known as open session in view with automatic session flushing. I recommend you do some reading on some of the main issues people face with GORM.
Proper use of discard may solve your issue. Discard your instance changes before you exit your controller.
For example:
if (!isAuthenticated()){
notAllowed()
userInstance.discard()
return
}
Edit
Based on conversation in the comments this perhaps may be the way to address your issue. A combination of discard and attach.
userInstance.discard()
def userBackup = User.findById(userInstance.id)
userInstance.role = userBackup.role
userInstance.active = userBackup.active
userInstance.hash = userBackup.hash
userInstance.authToken = userBackup.authToken
userInstance.attach()
I was helped by this method.
getPersistentValue
Example
def update(ShopItem shopItemInstance) {
if (shopItemInstance == null) {
notFound()
return
}
if (!shopItemInstance.itemPhoto){
shopItemInstance.itemPhoto =
shopItemInstance.getPersistentValue("itemPhoto");
}
if (shopItemInstance.hasErrors()) {
respond shopItemInstance.errors, view:'edit'
return
}
shopItemInstance.save flush:true
redirect(action: "show", id: shopItemInstance.id)
}
In your case:
userInstance.role = userInstance.getPersistentValue("role")
userInstance.active = userInstance.getPersistentValue("active")
userInstance.hash = userInstance.getPersistentValue("hash")
userInstance.authToken = userInstance.getPersistentValue("authToken")
It's better if you'll use the command objects feature. You can bind a command object with the request payload, validate it and than find and update the domain object.
You can find more details here:
http://grails.org/doc/2.3.x/guide/theWebLayer.html#commandObjects
And off the record you shoudn't use #Transactional in your controller. You can move that code into a service.
Eq:
def update(Long id, UserCommand cmd){
// Grails will map the json object into the command object and will call the validate() method if the class is annotated with #Validatable
}
I want to customize the default error message "This field is required" when using the "nonEmptyText" constaint in the Scala form helper.
Here is an example that I want to customize:
val form = Form(
tuple("email" -> nonEmptyText, "password" -> nonEmptyText)
verifying ("Invalid email or password.", result => result match {
case (email, password) => {
User.authenticate(email, password).isDefined
}
}))
Optimally in my conf/messages file I could provide a field-specific error:
error.email.required=Enter your login email address
error.password.required=You must provide a password
But in the worst case I would be happy with a wildcard message using the field name:
error.required=%s is required
#would evaluate to "password is required", which I would then want to capitalize
I saw this %s expression in some Play 1.x documentation but it does not seem to work anymore.
Thank you in advance for your help!
Try to drop the usage of a nonEmptyText and use a simple text field with a custom validation:
tuple(
"email" -> text.verifying("Enter your login email address", _.isDefined),
"password" -> text.verifying("You must provide a password", _.isDefined)
)
You can then move a step further and exchange the String inside the verifying clause for a call to the play.api.i18n.Messages object:
tuple(
"email" -> text.verifying(Messages("error.email.required"), _.isDefined),
"password" -> text.verifying(Messages("error.password.required"), _.isDefined)
)
Note that this is untested code, but it should point out the direction.
Good Luck
I want to add password confirmation to my project's registration page. But I'm having a problem with value of one of my fields. All the fields are ok but the password2 field, it's the confirmation password and is a transient field of User domain class. Here are my Domain, Controller and View codes
Domain class:
class User {
String fullName
String username
String email
String password
String password2
static transients = ['password2']
static constraints = {
username size: 5..15, blank: false, unique: true
email email: true, blank: false
password blank:false, size:5..15, matches:/[\S]+/, validator:{ val, obj ->
if (obj.password != obj.password2)
return 'user.password.dontmatch'
}
}
}
Part of the Controller:
if (request.method == 'POST') {
// create domain object and assign parameters using data binding
def u = new User(params)
if (! u.save()) {
// validation failed, render registration page again
flash.error = "error(s) creating user: Value of the confirmation password is: "+u.password2
//render(view:"/signup",model:[user:user])
return [user:u]
} else {
// validate/save ok, store user in session, redirect to homepage
session.user = u
redirect(controller:'home')
return
}
} else if (session.user) {
// don't allow registration while user is logged in
redirect(controller:'home')
return
}
And a part from the View:
<div><input class="pretty-input ${hasErrors(bean:user,field:'email','input-error')}" type="email" name="email" value="${user?.email}" ></input><span>— Enter email</span></div>
<div><input class="pretty-input ${hasErrors(bean:user,field:'password','input-error')}" type="password" name="password"></input><span>— Choose a password</span></div>
<div><input class="pretty-input ${hasErrors(bean:user,field:'password2','input-error')}" type="password" name="password2"></input><span>— Repeat the password</span></div>
My code works if I remove password's custom validation because password2's value is always null. I why this happens when all other fields works but password2 not.
Trasients fields are not bindables by default, thus you cannot pass params to fill it.
You can do this:
static constraints = {
... your code here
password2 bindable: true
}
But I recommend you to use a command object to validate that condition, it is clever.
I'm having problem inserting (or updating) new data to database using anorm in Play Framework 2.0. My model is a blog post defined in the following code:
case class Post(title: String,
content: String,
created: String,
lastUpdate: String,
writer: Long,
id: Long = 0)
Then I do this on the insert function:
def create(title: String, content: String, userId: Long) = {
DB.withConnection { implicit connection =>
SQL("INSERT INTO post (title, content, created, writer) VALUES ({title}, {content}, NOW(), {writer})")
.on(
'title -> title,
'content -> content,
'writer -> userId).executeUpdate()
}
And the form:
val postForm = Form(
tuple(
"title" -> nonEmptyText,
"content" -> nonEmptyText))
I did not have a userId field in the form because I don't trust the user's input on their own id. I get it from session. Anyway, that's why I can't directly put the validation code in postForm's declaration (because I think I can't access session both from the form and from model). And here's when it becomes ugly. If the post is invalid, anorm throws an Exception so I need to pass the error to user AFTER the fold function like:
postForm.bindFromRequest.fold(
formWithErrors => BadRequest(views.html.post.newPost(formWithErrors)),
newPost => {
try {
val userId = request.session.get("id").getOrElse("0")
models.Post.create(newPost._1, newPost._2, userId.toLong)
} catch {
case e => BadRequest(views.html.post.newPost(postForm.fill(newPost)))
}
Redirect(routes.Application.index)
})
First, the try-catch is ugly. Second, the BadRequest call don't work. What am I doing wrong? What is the best way to handle errors in insert / update? I have the same problem with a login form, but it's not as bad as this because I can actually handle errors at login form declaration.
Thanks before.
Assuming the error is on an integrity constraint like 0 not existing in the database, what about something like this ?
postForm.bindFromRequest.fold(
formWithErrors => BadRequest(views.html.post.newPost(formWithErrors)),
newPost => {
request.session.get("id").map({ userId=>
models.Post.create(newPost._1, newPost._2, userId.toLong)
Redirect(routes.Application.index)
}).getOrElse{
BadRequest(views.html.post.newPost(postForm.fill(newPost)))
}
}
)
You don't give invalid information to anorm and don't get an error ...
ps : I don't have a scala compiler on hand to check the syntax right now, I might have missed a paren or mixed a paren with a curly :)