Accessing session information inside a custom PartHandler in Play! Framework 2.0 - scala

In order to achieve a streaming upload I have written a custom PartHandler (Thread here ).
I now need to access a value that is stored inside the play! session inside the PartHandler.
How can I do that ?
Code sample:
def uploadFile() =
Action( parse.multipartFormData(myPartHandler) )
{ request =>
request.session.get("myValue") // <-- Recovering value is fine here
Ok("Done") }
def myPartHandler: BodyParsers.parse.Multipart.PartHandler[MultipartFormData.FilePart[Result]] = {
parse.Multipart.handleFilePart {
case parse.Multipart.FileInfo(partName, filename, contentType) =>
// ** Need to access a value in session here **
//request.session.get("myValue")...
// Handle part ...
Thanks!

With the help of other users of the play! framework google group, here is how to access the request inside a custom partHandler.
//Create a body parser
val myBodyParser = BodyParser { request =>
parse.multipartFormData(myPartHandler(request)).apply(request)
}
def uploadFile() = Action(myBodyParser)
{request =>Ok("Done")}
def myPartHandler(request: RequestHeader) : BodyParsers.parse.Multipart.PartHandler[MultipartFormData.FilePart[Result]] = {
parse.Multipart.handleFilePart {
case parse.Multipart.FileInfo(partName, filename, contentType) =>
println(request.session.get("myValueKey"));

Related

Gatling session - get attribute as Long

I am a new in Scala and got some problems with casting from String to Long. I try to get Gatling session value as Long in request. Before in exec() part, I try to set the userId value
def setUserId(): ChainBuilder = {
exec(session => session
.set("userId", Random.nextLong())
)
}
Next, in request creator I want to use it like that because I need a new userId every call:
object UserRequestCreator {
def sampleUserRequest(currency: String): Request = {
Data data = new Data()
data.setUserId("${userId}".toLong)
data.setCurrency(currency)
}
}
Test scenario:
exec(setUserId())
.exec(http("postUser")
.post(endpointUser).asXml
.headers(headers)
.body(StringBody(toXmlString(sampleUserRequest("EUR"), classOf[Request])))
.check(status.is(200))
but receive error:
java.lang.NumberFormatException: For input string: "${userId}"
How to fix that in Scala?
I also try Long.valueOf, JLong.parseLong("${userId"}, 16), Try(BigDecimal(...)) and more but nothing can help. I think the problem is with $ symbol, but I don't see any different way to get this value from the session. Maybe it is possible to store Long in the Gating session?
From the documentation and based on your current code, one way to do it is like that:
// with a function payload
http("name").post("/")
.body(StringBody(session => s"""{ "foo": "${session("dynamicValueKey").as[String]}" }"""))
Thus, in your case:
StringBody(session => toXmlString(sampleUserRequest(session)("EUR"), classOf[Request])
def sampleUserRequest(session: Session)(currency: String): Request = {
//...
data.setUserId(session("userId").as[Long])
}

Stripe Webhook Verification Error with Play Framework

I am trying to set up stripe payment with a play framework application. I am having issues with setting up the webhook.
com.stripe.exception.SignatureVerificationException: No signatures found matching the expected signature for payload
This is the error I keep getting when I try and construct the event sent from stripe. I print out the values for the body and the signature and they look like they should be correct. This is the code I am using to collect the webhook.
def webhook = Action { implicit request: Request[AnyContent] =>
println(request.headers.toMap)
val bodyOp:Option[JsValue] = request.body.asJson
val sigOp:Option[String] = request.headers.get("Stripe-Signature")
var event: Event = null
if (bodyOp.isEmpty || sigOp.isEmpty) {
WebhookController.logger.write("EMPTY BODY OR SIG body-"+bodyOp+" sig-"+sigOp,Logger.RED)
BadRequest
} else {
val body = bodyOp.get.toString
val sig = sigOp.get
println(body)
println(sig)
try {
event = Webhook.constructEvent(body, sig, "whsec_5XwS8yCNOcq1CKfhh2Dtvm8RaoaE3p7b")
val eventType: String = event.getType
eventType match {
case "customer.subscription.deleted" => deleteCustomer(event)
case "invoice.payment.succeeded" => successPayment(event)
case "invoice.payment.failed" => failedPayment(event)
case _ => WebhookController.logger.write("UNKNOWN " + event, Logger.RED)
}
Ok("")
} catch {
case e: JsonSyntaxException =>
e.printStackTrace()
WebhookController.logger.write("ERROR" + e.getMessage +" "+exceptionToString(e), Logger.RED)
BadRequest
case e: SignatureVerificationException =>
e.printStackTrace()
WebhookController.logger.write("ERROR" + e.getMessage + " "+exceptionToString(e), Logger.RED)
WebhookController.logger.write("SIG ERROR header-"+e.getSigHeader+" status code-"+e.getStatusCode,Logger.RED )
BadRequest
}
}
}
Karllekko was on the right track. Play framework automatically parsed it as a json which caused the error. request.body.asText didn't work because the content-type header value was set to json. Tolarant Text would have worked except for stripe sends their webhooks with utf-8 and tolarant text doesn't parse with utf-8. So I ended up having to use a RawBuffer and turning that into a String (https://www.playframework.com/documentation/2.6.x/ScalaBodyParsers)
class WebhookController #Inject()(parsers: PlayBodyParsers) extends Controller() {
def webhook = Action(parsers.raw) { implicit request: Request[RawBuffer] =>
val bodyOp = request.body.asBytes()
val sigOp:Option[String] = request.headers.get("Stripe-Signature")
var event: Event = null
if (bodyOp.isEmpty || sigOp.isEmpty) {
BadRequest
} else {
val body = bodyOp.get.utf8String
val sig = sigOp.get
try {
event = Webhook.constructEvent(body, sig, secret)
...
...
You need to pass the raw body of the request exactly as received to constructEvent, not the JSON parsed version.
I believe that should be
event = Webhook.constructEvent(request.body.asText, sig, "whsec_5XwS8yCNOcq1CKfhh2Dtvm8RaoaE3p7b")
in this case?
You can have a look at the examples from Stripe as well[0]. But the core issue is that you must sign the exact string sent by Stripe, without any interference by your code or a framework in the middle.
[0]- https://github.com/stripe/stripe-java/blob/master/src/test/java/com/stripe/net/WebhookTest.java
zamsler's solution works for me. Thank you! Saved me a lot of time. For anyone who wants the Play 2.8 Java version:
#BodyParser.Of(BodyParser.Raw.class)
public Result webhook(Http.Request request) {
String payload = request.body().asBytes().utf8String();
String sigHeader = request.getHeaders().get("Stripe-Signature").get();
String endpointSecret = "xxxxxxxx";
try {
event = Webhook.constructEvent(payload, sigHeader, endpointSecret);
} catch (SignatureVerificationException e) {
e.printStackTrace();
// Invalid signature
return badRequest();
}
...
...
}

Akka-http & scribe for linkedin API: set/get cookie without session (scala)

I am using akka-http for bulding a REST API. (I am new to build REST web services).
I don't know how I can get and set cookie without using a session. This cookie must contain encrypt token access. I don't use Play or spray.
My code for the moment is:
lazy val signin = path("signin") {
get {
/* create the OAuthService object with a callback URL*/
val service = buildService()
/* get the request token*/
val requestToken = service.getRequestToken
/* create the cookie */
val jwtCookieEncrypted = tokenUtil.createLinkedinTokenSecret(requestToken)
val cookie = HttpCookie("jwtTokenCookie", jwtCookieEncrypted)
/* making the user validate our requestToken by redirecting him to the following URL*/
val authURL = service.getAuthorizationUrl(requestToken)
redirect(authURL, StatusCodes.TemporaryRedirect)
}
}
lazy val callback = path("callback") {
// extract cookie with the jwtTokenCookie name
cookie("jwtTokenCookie") { cookiePair =>
complete(s"The logged in user is '${cookiePair.name}'")
}
get {
parameters('code, 'state) { (code, state) => // must come from cookie and not request parameters
/* create the OAuthService object with a callback URL*/
val service = buildService()
/* get the request token*/
val requestToken = new Token(code, state)
if(state == tokenUtil.decryptLinkedinToken(requestToken.getSecret).getOrElse("")) "continue" else "throw error"
val verifier = new Verifier(state)
/* get the access token
(need to exchange requestToken and verifier for an accessToken which is the one used to sign requests)*/
val accessToken = service.getAccessToken(requestToken, verifier)
logger.debug(accessToken.getRawResponse)
/* sign request*/
val ResourceUrl = Settings.LinkedIn.ResourceUrl
val request = new OAuthRequest(Verb.GET, ResourceUrl)
service.signRequest(accessToken, request)
val response = request.send
if (response.getCode == StatusCodes.OK.intValue) complete(response.getBody)
else complete(int2StatusCode(response.getCode))
}
}
}
signin ~ callback
Check the akka doc. In your response you can include the header. In your case, maybe with redirect it´s not so simple. But you could complete the signing request returning a 308 Http code with the Location Header pointing to your oauth2 Auth server.
Is it better ?
path("signin") {
get {
val service = buildService()
val requestToken = service.getRequestToken
val authURL = service.getAuthorizationUrl(requestToken)
val requestTokenCrypted = tokenUtil.createLinkedinToken(requestToken)
val cookie = HttpCookie("abcde", requestTokenCrypted.getSecret)
setCookie(cookie) {
complete(HttpResponse(
status = StatusCodes.TemporaryRedirect,
headers = List(Location(authURL))
))
}
}
}

How to proxy an HTTP method with play framework 2.5?

I have 2 simple APIs:
GET /users/me/photos controllers.api.UserController.getMyPhotos
GET /users/:userId/photos controllers.api.UserController.getPhotos(userId: Int)
Here's getPhotos:
def getPhotos(userId: Int) = SecuredAction.async {
logger.info(s"Searching for user $userId's photos")
userPhotosRepo.findByUserId(userId).map {
photos => Ok(Json.toJson(photos))
}
}
Here's getMyPhotos:
def getMyPhotos = SecuredAction.async { request =>
request.identity.id.map { currentUserId =>
logger.info(s"Searching for current user's photos")
getPhotos(currentUserId) // doesn't work
}.getOrElse(Future.successful(InternalServerError))
}
How can I make getMyPhotos proxy through to getPhotos without creating a helper method they both call?
Here you can use reverse routing provided by Play Framework
[full package].routes.[controller].[method]
In your case
routes.api.UserController.getPhotos(request.identity.id)
If you want the result of first action
val ans: Result = Redirect(routes.api.UserController.getPhotos(request.identity.id))
I hope that's what you were trying to ask.
EDIT:
For your concern this should be a proper way to do it
def getPhotos(userId: Long) = SecuredAction.async {
userPhotosRepo findByUserId(userId) map {
photos => Ok(Json.toJson(photos))
}
}
def getMyPhotos = SecuredAction.async { request =>
request.identity.id map { id =>
Redirect(routes.HomeController.getPhotos(id))
}
}

Custom spray.io directive to validate request header value

I am new to spray and I am trying to write a custom directive. I would like the directive to reject the request if the header value is not valid otherwise leave the request alone.
I've tried to absorb this page:
http://spray.io/documentation/1.1.2/spray-routing/key-concepts/directives/
Specifically, the part about the responder chain. I'm trying to create something at the level of the bar Directive in the illustration. I'm just not getting how to pass the context unchanged to the inner route.
My else block below is not correct but expresses what I am trying to do. I just can't figure out how to implement it.
Any help would be greatly appreciated.
trait ApiKeyDirective {
import spray.routing.directives.HeaderDirectives._
import spray.routing.directives.BasicDirectives._
def validateApiKey(): Directive1 = {
headerValueByName("api-key") {key =>
val valid = key == "123"
if (!valid) reject() else pass
}
}
}
object ApiKeyDirective extends ApiKeyDirective
You can combine
headerValueByName:
def headerValueByName(headerName: String): Directive1[String]
with validate:
def validate(check: ⇒ Boolean, errorMsg: String): Directive0
For example:
def validateApiKey(route: Route) =
headerValueByName("api-key") { key =>
validate(key == "123", "Invalid API key") {
route
}
}
or without validate:
def validateApiKey(route: Route) =
headerValueByName("api-key") { key =>
if (key == "123")
route
else
reject(ValidationRejection("Invalid API key"))
}
Usage:
lazy val route = ...
... ~
pathPrefix("test_directive") {
get {
validateApiKey {
complete("ok")
}
}
} ~
...
Test from cmd/shell:
# curl http://localhost:8080/test_directive
Request is missing required HTTP header 'api-key'
# curl http://localhost:8080/test_directive -H 'api-key: bad'
Invalid API key
# curl http://localhost:8080/test_directive -H 'api-key: 123'
"ok"
I'm just not getting how to pass the context unchanged to the inner
route.
Spray does that for you!
Your code is mostly correct, there are just 2 simple problems to fix!
Firstly, you need to flatMap headerValueByName("api-key") directive.
Secondly, the return type will be Directive0 because the directive won't provide any value.
So final code would look like this:
object ApiKeyDirective {
import spray.routing.Directives._
val validateApiKey: Directive0 =
headerValueByName("api-key").flatMap { key =>
val valid = key == "123"
if (!valid) reject() else pass
}
}
Also, I recommend you to add a custom rejection to reject() block so that API users will be informed when their api key is invalid.