Reputation: 13
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
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