Lift Authentication - scala

I want to create an authentication route for my Lift Application.
Create a route, for instance www.myapp.com/user/login
I am not using Lift forms/templating. The forms are rendered in JS.
Send a post request with email and password.
Call Lift authentication when that POST request is received.
Use the Users.login(email, password) method to validate the credentials.
Q:
How do I tell Lift to authenticate the credentials incoming via /user/login?

This is overly simplistic, but something like this will allow you to create a url that you can post to. The JSON extraction is not very safe, but should give you an idea of how this might work.
In Boot.scala
LiftRules.dispatch.append(new RestHelper{
serve {
case JsonPost("user" :: "login" :: Nil, (json, _)) =>
//extract JSON from json object to get username and password
val userEmail:String = (json \ "username").extract[String]
val password = (json \ "password").extract[String]
User.login(userEmail, password) match {
case Full(r) =>
User.current(true)
InMemoryResponse(Array(), Nil, Nil, 200)
case _ => ForbiddenResponse
}
}
})
In User.scala
object User {
object loggedIn extends SessionVar[Boolean](false)
}
Then you can use if(User.loggedIn.get){ ... } to test if the user is logged in anywhere. This will work for anything added to the stateful dispatch, if you use LiftRules.statelessDispatch the session will not exist.

I created a quick working example which you can checkout on github. It uses the code you provided, so hopefully it will be pretty straightforward. You can take a look here: https://github.com/jcern/lift_httpauth
But, essentially to add the code to the sitemap, you'd just need to add the following to Boot.scala:
Menu("Login Required") / "user" / "login"
And make sure there is a user/login.html in your webapp root.

Related

How can mock a Keycloak token for unit test in ScalaTest

The app I'm working (in Scala) keep a register of all the users that login using Keycloak, for that creates a user for every new keycloak user using the information on the keycloak authorization token
private def decodeToken(token: String): Try[User] = {
Try { AdapterTokenVerifier.verifyToken(token, keycloakDeployment) } match {
case Success(accessToken: AccessToken) =>
Try (User(name = accessToken.getName, userName = accessToken.getPreferredUsername, email = Some(accessToken.getEmail))
case Failure(ex) =>
throw new Exception("authentication failed")
}}
My problem is the following, I need to do unit tests to my code, for that is used ScalaTest, how can a mock a token of keycloak inside the test so this code can be tested.
I already write the test and works but I have to pass manually a token, that means, any other time I run the test if the token expire the test files, also files if the test is run in another computer, also fails in gitlab (don't pass pipelines)
Note: I'm new with scala and with keycloak, also English is not my firs language, so it's understandable that some things are not very clear, if you think you can help me, feel free to ask anything
Note 2: With that user that is created with the Keycloak token, other things are done but this is the most relevant to my problem

Play framework, Scala: authenticate User by Role

I've user roles: user, manager, admin. I need to authenticate them in controllers (methods). For example only admin can delete (now it looks like this, need to change that only admin should have permission):
def deleteBook(id: Int) = DBAction {
findById(id) match {
case Some(entity) => {
books.filter(_.id === id).delete
Ok("")
}
case None => Ok("")
}
}
I've many controllers and methods. I need to authenticate before process request (for example deleting book). My routes file contains:
...
DELETE /books/:id #controllers.Book.deleteBook(id: Int)
...
Some routes are only accessible to admin and manager. Some are for all types of users.
I'm currently seeing deadbolt2scala authorization module for play.
Can you recommend best way to authenticate multirole users in playframework scala?
I've managed to do this by using StackableControllers provided by https://github.com/t2v/stackable-controller
Basically, I use a basic access control list provided by my application.conf. I start by checking if there is a user in my request. If there is one, I can check if he has sufficient access rights to perform the action.
Such a feature may be implemented using BodyParser composition too. I've never done that, though, so someone else's advice may be better for you.

securesocial redirect user on save

I'm trying to make authentication work with securesocial plugin for Play! framework 2.2.
I'm implementing my own version of UserService.
When someone logs into my website for the first time using some identity (Facebook, Twitter or everything else) I want to redirect him to a Registration Page to collect more informations about him and to make him accept my Terms and Conditions.
How can I achieve this?
Thank you for your help.
A.M.
def index = SecuredAction { implicit request =>
// Check if user is logging in for the first-time... or other logic
if(true){//If logic for redirection meets
Redirect(routes.Application.step2)
}else{
println("Home")
Ok(views.html.index(request.user))
}
}
def step2 = SecuredAction { implicit request =>
//Gather Other data... point to another form, your choice
Ok(views.html.step2(request.user))
}
I presume your files look like these..
securesocial.conf
securesocial {
onLoginGoTo=/
routes.conf
GET / controllers.Application.index
GET /step2 controllers.AmazonController.step2 //Additional Route

Scala/Play http authentication issue - getting user name and password from request

I'm using Firefox "REST Easy" plugin to test HTTP authentication in my Play application. This is how the request URL looks like in Firebug:
http://username:password#localhost:9001/test
The method in my Play controller looks like this:
def test = Action { implicit request =>
request.headers.get("Authorization") match {
case Some(header) => println(header)
case None => {
println("send user name and password")
Unauthorized.withHeaders("WWW-Authenticate" -> "Basic realm=\"myrealm\"")
}
}
Ok("")
}
I'm getting "send user name and password" in my console and nothing else happens. What am I doing wrong and how do I get the user name and password from the request?
Your code works fine, the problem is in your used browser plugin. Somehow it doesn't work with basic auth because if you add a println(request.headers) call as the first line of your action body you can see it's not in there, while if you use curl it's there!
I used the following commind with your given code:
curl --user name:password http://localhost:9000/test
and it results in:
Basic bmFtZTpwYXNzd29yZA==
So it works!

Redirect to referer after a POST Request

I have a web application in Play. The web application consists of several pages. In every page there is a small flag that enables the user to change the language (locale) from german to english and back.
I handle this with a redirect to referer:
def referer(implicit request: Request[AnyContent]) =
request.headers.get(REFERER).getOrElse(mainUrl)
def locale(l: String) = Authenticated { user =>
implicit request =>
Redirect(referer).withCookies(Cookie(LANG, if (l == "de" || l == "en") l else "de"))
}
It is working fine. Well, at least for GET requests.
I have a specific page where the user has to input data in a form. This form is then POSTed to the server. Were errors found, the form is displayed again with the error messages, as usual. Now, if the user wants to change the language (by clicking on the flag), the redirect to referer does not work, because it tries to use a GET request, and Play complains that a GET route does not exist for this method (which is true).
I am solving this by caching the form and defining another method where the form is taken from the cache:
# User data is POSTed to the server
POST /create/insert controllers.MyCreate.insert()
# After a redirect the cached form is displayed again
GET /create/insert controllers.MyCreate.insertGet()
It works, but I don't like this solution. It does not seem normal to have to create another entry in the routes and another method just to adress this problem. I would need to add this hack for every POST route in my application!
Is there a more elegant solution to this?
You could change it into something like this (untested):
def changeLang(lang:String, returnUri:String) = Action {
Redirect(returnUri)
.withCookies(Cookie(LANG, if (lang == "de" || lang == "en") lang else "de"))
}
In you template you would output the route to changeLang in the link, you can get the uri via the request
#routes.Application.changeLang("en", request.uri).url
I suggest you make request implicit in your action and define it as implicit in your template so you don't need to pass it on to each template.
// in the controller
def myUrl = Action { implicit request =>
Ok(views.html.myTemplate("something"))
}
// in the template
#(title:String)(implicit request:play.api.mvc.RequestHeader)
Edit
As for the POST requests, it common (for these types of framework) to have POST requests simple handle stuff and then redirect to another page. The usual flow is like this:
Form submits to a handler
Handler does something with the form information
Handler redirects to a page
An example:
// Hooked up to a GET route
def edit(id:Long) = Action {
// render the view with a form that displays the element with given id
// if the flash scope contains validation information, use that in display
}
// Hooked up to a POST route
def editHandler = Action {
// validate the form
// if validation succeeds
// persist the object
// redirect to edit
// else
// put the form information into the flash scope
// put any validation messages into the flash scope
// redirect to edit
}
If you do not want to use this flow you need to have both a GET and POST route anyway. The user might do a page reload on the resulting page.