File Upload results in 400 BadRequest


#1

File upload along with enforcing authentication from silhouette does not work - I get 400 Bad Request [Unexpected end of input]. Here is the filter I am using and also indicated where in this code it makes a difference:

package utils

import akka.stream.Materializer
import com.google.inject.Inject
import com.mohiva.play.silhouette.api.actions.UserAwareRequest
import com.mohiva.play.silhouette.api.{ Env, Silhouette }
import play.api.mvc.Results._
import play.api.mvc.{ AnyContent, Result, RequestHeader, Filter }
import play.api.{ Environment, Mode }
import utils.auth.DefaultEnv

import scala.concurrent.Future

class MySecuredFilter @Inject() (silhouette: Silhouette[DefaultEnv])(implicit val mat: Materializer) extends Filter {

override def apply(next: RequestHeader => Future[Result])(
request: RequestHeader): Future[Result] = {

val action = silhouette.UserAwareAction.async { r =>
  request.path match {
    case "/admin" if r.identity.isEmpty => Future.successful(Unauthorized)
    case _ => next(request)
  }
}
action(request).run // multipart/form-data file upload => 400 BadRequest
//next(request)     // WORKS !!!

}
}

I changed the template https://github.com/mohiva/play-silhouette-seed

I am really stuck on this issue - can some please help ?
If necessary I can share the complete code.

Thanks
Sarav


#2

Hi,

If next(request) works, why do you not use it?

Best regards,
Christian


#3

But then Silhouette UserAware action won’t be executed right ? I want “action” to be “applied”.

Thanks
Sarav


#4

Hi,

Yes, Sorry, You are right!

I’ve no idea what’s going on in this case. Have you tried to use a default Play action, to see if the error comes from Silhouette?

Best regards,
Christian


#5

Hi Christian,

Yes, following, for example, works:

def upload() = play.api.mvc.Action.async(parse.multipartFormData(handleFilePartAsFile)) { implicit request =>

If I change to “silhouette.SecuredAction.async()” it doesn’t. My Silhouette version is 4.0.0 and play version is 2.5.4 (but issue is seen with latest play version too).

Thanks
Sarav


#6

I meant in the filter. If you change:

val action = silhouette.UserAwareAction.async { r =>
  request.path match {
    case "/admin" if r.identity.isEmpty => Future.successful(Unauthorized)
    case _ => next(request)
  }
}

action(request).run

with

val action = Action.async { r =>
  next(request)
}

action(request).run

Does the file upload work?

Have you tried:

silhouette.SecuredAction.async(parse.multipartFormData(handleFilePartAsFile))

?

I’m sure the file upload for a Silhouette action, not the filter approach, works definitely.


#7

Hi!

The following dummy filter produces the same error (400 Bad Request [Unexpected end of input]) when submitting a multi-part encoded form to either a Play Action or a Silhouette UserAwareAction.

class NopFilter @Inject()(silhouette: Silhouette[AuthEnv],
                          implicit val mat: Materializer) extends Filter {
  override def apply(next: (RequestHeader) => Future[Result])(req: RequestHeader): Future[Result] =
    Action.async(r => next(req))(req).run
}

class DummyController @Inject()(val silhouette: Silhouette[AuthEnv]) extends Controller {
  def playAction = Action.async(r => ???)
  def silhouetteAction = silhouette.UserAwareAction.async(r => ???)
}

I deduce that it might be a problem with Play and not only Silhouette-related when using any Action in a filter.

Do you have an alternative to using an UserAwareAction to obtain the current identity in a filter?

Thanks!


#8

As a multi-part request body can only be read once, I implemented a dummy BodyParser for use in my Filter, leaving it unread for the final Action.

class ZeroZilchZipNadaBodyParser(implicit val ec: ExecutionContext) extends BodyParser[AnyContent] {
  override def apply(r: RequestHeader) =
    Accumulator.source[ByteString].mapFuture(_ => Future.successful(Right(AnyContentAsEmpty)))
}

class LeFilter @Inject()(silhouette: Silhouette[AuthEnv],
                         implicit val mat: Materializer)
                        (implicit val ec: ExecutionContext) extends Filter {
  override def apply(next: (RequestHeader) => Future[Result])(req: RequestHeader): Future[Result] =
    silhouette.UserAwareAction.async(new ZeroZilchZipNadaBodyParser)(r => next(req))(req).run
}

class LeController @Inject()(val silhouette: Silhouette[AuthEnv])
                            (implicit val ec: ExecutionContext) extends Controller {
  def action = silhouette.UserAwareAction.async(r => ???)
}