I'm trying to make all my rest api endpoints return the token with which the user used to make the request (already exists in all request).
This is how my basic endpoint looks like:
def getAll(meta: MetaData): Action[AnyContent] = deadbolt.Restrict(role)(parse.default) {
implicit request => myService.getAll(meta).map(result => {
val results: Seq[Resp] = result._2.map(res => Mapper(res))
Ok(Json.toJson(MyListResponse.apply(meta, result._1, results)))
})
}
How can I add to this my response the token's information received from in the request?
Thanks!
As you described in comment section you need to get token from query parameter and add it response header. Let's suppose that this token is optional, hence might be absent, so this can be achieved something like:
implicit request => myService.getAll(meta).map { result =>
// Fetch optional token as query parameter. `token_parameter` - parameter key
val requestToken = request.queryString.get("token_parameter").flatMap(_.headOption)
val results: Seq[Resp] = result._2.map(res => Mapper(res))
val body = Json.toJson(MyListResponse.apply(meta, result._1, results))
// Add token to header if it is present
requestToken.fold(Ok(body))(token => Ok(body).withHeaders("token_header" -> token))
}
Update
In order to apply this logic to ALL the routes, you can use Play Filters feature. Please, see doc for more details: https://www.playframework.com/documentation/latest/ScalaHttpFilters
What you need to do:
1) Implement your own Play Filter. It would look something like next:
import javax.inject.Inject
import akka.stream.Materializer
import play.api.mvc._
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
class TokenFilter #Inject() (implicit val mat: Materializer, ec: ExecutionContext) extends Filter {
def apply(nextFilter: RequestHeader => Future[Result])
(requestHeader: RequestHeader): Future[Result] = {
nextFilter(requestHeader).map { result =>
val requestToken = requestHeader.queryString.get("token_parameter").flatMap(_.headOption)
requestToken.fold(result )(token => result.withHeaders("token_header" -> token))
}
}
}
2) Wire filter to rest of application. For instance, via adding next config in application.conf
play.http.filters += com.yourcomany.TokenFilter
Hope this helps!
Related
I've added keycloak authentication to my Playframework project, but it's implemented only for one Action. I'm trying to create a custom Action class which will be validating the JWT and which I'll inject into all classes.
My current endpoint code:
val configData: Config = config.get[Config]("auth.keycloak")
val keycloakConfig = KeycloakConfig(configData)
val kcContext = KeycloakHelperContext(keycloakConfig)
def getUserProps() = Action {
implicit request: Request[AnyContent] =>
val user = kcContext.dpKcAuthentication.validateJWT
Ok(write(Map("user" → user))).as(JSON)
}
The custom actionimpl I'm trying to create:
val configData: Config = config.get[Config]("auth.keycloak")
val keycloakConfig = KeycloakConfig(configData)
val kcContext = KeycloakHelperContext(keycloakConfig)
override def invokeBlock[A](request: Request[AnyContent], block: Request[A] => Future[Result]): Future[Result] = {
val user = kcContext.dpKcAuthentication.validateJWT(request)
block(request)
}
And it says:
Method 'invokeBlock' overrides nothing
It happens when I pass request to validateJWT method. I don't do it in my controller because I simply say that the request is implicit and it's passed there.
For current implementation, I need to make the request inside invokeBlock implicit or find a way to pass it validateJWT method.
I'm working on a Scala Play application and need many Controller actions disabling caching of the browser by setting parameters in the HTTP headers of the Response. I decided to create a NoCache composite action and since I am also using Deadbolt-2 (and need a Deadbolt-2's AuthenticatedRequest[_]) it looks like this:
package action
import be.objectify.deadbolt.scala.AuthenticatedRequest
import play.api.http.HeaderNames
import play.api.mvc._
import scala.concurrent.Future
import scala.util.Success
case class NoCache[A](action: Action[A]) extends Action[A] with HeaderNames {
def apply(request: AuthenticatedRequest[A]): Future[Result] = {
action(request).andThen {
case Success(result) => result.withHeaders(
(CACHE_CONTROL -> "no-cache, no-store, must-revalidate"),
(PRAGMA -> "no-cache"),
(EXPIRES -> "0")
)
}
}
lazy val parser = action.parser
}
but then it won't compile trying to mix in this Action into my Controller action implementations e.g.
def link = deadbolt.SubjectPresent()() andThen NoCache() { implicit request =>
or
def link = NoCache(deadbolt.SubjectPresent()()) { implicit request =>
but can't see how to compose them ...
I found how to do it for a single action:
def index = NoCache {
deadbolt.WithAuthRequest()() { implicit request =>
Future {
Ok(views.html.index(userService))
}
}
}
However, I still haven't found how to apply NoCache to the entire controller class.
I wonder, how to get a body from the request if in the doc:
trait RequestHeader extends AnyRef
The HTTP request header. Note that it doesn’t contain the request body yet.
That seems from very 2.0 version ..
Trying to get example of async handing the request's body. In order to log it to file.
object AccessLoggingFilter extends EssentialFilter {
def apply(inputAction: EssentialAction) = new EssentialAction { request =>
val accessLogger = Logger("access")
def apply(requestHeader: RequestHeader): Iteratee[Array[Byte], Result] = { ...
Logger.info(s"""Request:
Body = ${requestHeader.???} """)
There are some philosophical answers on SO, here for example. But I would not call it answer..
Yes, play doesn't allow to access the body of the request in the filter stage.
If you only want to log the body, you can create a new action for that and compose it.
This is an example from play 2.6
def withLogging(action: Action[AnyContent]): Action[AnyContent] = {
Action.async(action.parser) { request =>
request.body match {
case AnyContentAsJson(json) => Logger.info("JSON body was: " + Json.stringify(json))
case _ => //implement more logging of different body types
}
action(request)
}
}
def foo = withLogging(Action.async(cc.parsers.anyContent) { implicit request =>
// do your stuff
}))
If you have only json endpoints, you can write a specific action for that.
I'm trying to integrate Deadbolt2 into my play framework 2.4 application.
A have following piece of code in my controller
import be.objectify.deadbolt.scala.{ActionBuilders, AuthenticatedRequest, DeadboltActions}
import be.objectify.deadbolt.scala.cache.HandlerCache
import play.api.data.Form
import play.api.data.Forms._
import play.api.mvc.{AnyContent, _}
import modules.user.security.{Authenticator, HandlerKeys, MyDeadboltHandler}
class Login #Inject() (deadbolt: DeadboltActions, handlers: HandlerCache, actionBuilder: ActionBuilders) extends Controller {
//...
def login = deadbolt.SubjectNotPresent() {
Action { implicit request =>
Ok(login(loginForm))
}
}
}
And I got error missing parameter type. I'm following deadbolt examples, which helps me a lot, but I can't figure out how to pass implicit request into action.
My template begins like this:
#(loginForm: Form[LoginForm])(implicit flash: Flash)
Using Action directly without deadbolt.SubjectNotPresent() works well.
One persistent question that keeps coming up is "how do I get the subject in an authorized action?". As of Deadbolt 2.5, the request passed into an action has been replaced with an AuthenticatedRequest which contains an Option[Subject]. As users of 2.4 also want this feature, it has been included in 2.4.4 as a breaking change.
The following examples use SubjectPresent as an example, but the same change applies to all authorization constraints.
When using action builders, in place of
def index = actionBuilder.SubjectPresentAction().defaultHandler() { implicit request
Ok(accessOk())
}
we now have
def index = actionBuilder.SubjectPresentAction().defaultHandler() { authRequest =>
Future {
Ok(accessOk())
}
}
When using action composition, in place of
def index = deadbolt.SubjectPresent() { implicit request
Action {
Ok(accessOk())
}
}
we now have
def someFunctionA = deadbolt.SubjectPresent()() { authRequest =>
Future {
Ok("Content accessible")
}
}
The getSubject() function of the DeadboltHandler trait now takes an AuthenticatedRequest instead of a Request.
override def getSubject[A](request: AuthenticatedRequest[A]): Future[Option[Subject]] =
request.subject match {
case Some(user) => Future {request.subject}
case None => // get from database, identity platform, cache, whatever
}
What this means for your app is
def login = deadbolt.SubjectNotPresent() {
Action { implicit request =>
Ok(login(loginForm))
}
}
becomes
def login = deadbolt.SubjectNotPresent()() { authRequest =>
Future {
Ok(login(loginForm))
}
}
I'm trying to build a REST API using play 2.0. I have a User case class that contains some fields (like username & password) that shouldn't be updatable by the updateMember method.
Is there a good, functional way, of dealing with multiple Options somehow, because request.body.asJson returns an Option[JsValue], and my user lookup also returns an Option:
package controllers.api
import org.joda.time.LocalDate
import play.api.Play.current
import play.api.db.slick.{DB, Session}
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.functional.syntax._
import models.{Gender, User, UserId}
import repositories.UserRepository
object Member extends Controller {
def updateMember(id: Long) = Action {
DB.withSession {
implicit session: Session =>
val json: JsValue = request.body.asJson // how to deal with this?
val repository = new UserRepository
repository.findById(new UserId(id)).map {
user =>
def usernameAppender = __.json.update(
__.read[JsObject].map { o => o ++ Json.obj("username" -> user.username) }
)
json.transform(usernameAppender) // transform not found
Ok("updated")
}.getOrElse(NotFound)
}
}
}
I could move the map call to where I try to parse the request, but then inside there I guess I'd need another map over the user Option like I already have. So in that style, I'd need a map per Option.
Is there a better way of dealing with multiple Options like this in FP?
You're basically dealing with a nested monad, and the main tool for working with such is flatMap, particularly if both options being None has the same semantic meaning to your program:
request.body.asJson.flatMap { requestJson =>
val repository = new UserRepository
repository.findById(new UserId(id)).map { user =>
def usernameAppender = __.json.update(
__.read[JsObject].map { o => o ++ Json.obj("username" -> user.username) }
)
requestJson.transform(usernameAppender)
Ok("updated") // EDIT: Do you not want to return the JSON?
}
}.getOrElse(NotFound)
But it might also be the case that your Nones have different meanings, in which case, you probably just want to pattern match, and handle the error cases separately:
request.body.asJson match {
case Some(requestJson) =>
val repository = new UserRepository
repository.findById(new UserId(id)).map { user =>
def usernameAppender = __.json.update(
__.read[JsObject].map { o => o ++ Json.obj("username" -> user.username) }
)
requestJson.transform(usernameAppender)
Ok("updated")
}.getOrElse(NotFound)
case None => BadRequest // Or whatever you response makes sense for this case
}