SetSever
SetSever

Reputation: 13

Object required: play.api.mvc.Result error

I want to secure my app with this

def Secured[A](username: String, password: String)(action: Action[A]) = Action(action.parser) { 
 request =>   request.headers.get("Authorization").flatMap { authorization =>
    authorization.split(" ").drop(1).headOption.filter { 
       encoded => new String(org.apache.commons.codec.binary.Base64.decodeBase64(encoded.getBytes)).split(":").toList match {
        case u :: p :: Nil if u == username && password == p => true
        case _ => false
      }
    }.map(_ => action(request))   }.getOrElse {
    Unauthorized.withHeaders("WWW-Authenticate" -> """Basic realm="Secured"""")   } }

But at getOrElse part, I'm getting the following error:

type mismatch; found : Object required: play.api.mvc.Result

What is wrong?

Upvotes: 0

Views: 719

Answers (1)

Odomontois
Odomontois

Reputation: 16308

Trouble here that action(request) is returning Future[Result] not just Result. So type of whole expression before getOrElse is Option[Future[Result]] and it awaits Future[Result] as parameter to getOrElse.

So at first you can wrap your Unauthorized.withHeaders thing into Future.successfull( ... )

Next thing is that result of whole expression then is Future[Result] and it's not correct argument type for the Action.apply(bodyParser)( ... ), but there is Action.async method that could handle it.

So whole corrected block with minimum type corrections \ refactoring is

import org.apache.commons.codec.binary.Base64

def secured[A](username: String, password: String)(action: Action[A]) =
  Action.async(action.parser) {
    request => request.headers.get("Authorization").flatMap { authorization =>
      authorization.split(" ").drop(1).headOption.filter { encoded =>
        val bytes = Base64.decodeBase64(encoded.getBytes)
        new String(bytes).split(":").toList match {
          case u :: p :: Nil if u == username && password == p => true
          case _ => false
        }
      }.map(_ => action(request))
    }.getOrElse {
      Future.successful(
        Unauthorized.withHeaders(
          "WWW-Authenticate" -> """Basic realm="Secured""""))
    }
  }

Further sugar injections could lead to even more readable version:

import org.apache.commons.codec.binary.Base64

def secured[A](username: String, password: String)(action: Action[A]) =
  Action.async(action.parser) { request =>
    val result = for {
      authorization <- request.headers.get("Authorization")
      Array(_, encoded, _*) <- Some(authorization.split(" "))
      bytes = Base64.decodeBase64(encoded.getBytes)
      credentials = new String(bytes).split(":").toList
      List(`username`, `password`) <- Some(credentials)
    } yield action(request)

    result.getOrElse {
      Future.successful(
        Unauthorized.withHeaders(
          "WWW-Authenticate" -> """Basic realm="Secured""""))
    }
  }

Note that unpacking in the left hand side of <- is transformed to filter with match

Upvotes: 2

Related Questions