I am trying to create a custom directive that I can later use to validate user roles like so:
val route = put & authorize(ADMIN) {
// do sth
}
or
val route = put {
authorize(ADMIN) {
//do sth
}
}
. Here is what I got so far:
def authorize(role: String): Directive0 = {
extractToken { t: String =>
validateToken(t) {
extractRoles(t) { roles: Seq[String] =>
validate(roles.contains(role), s"User does not have the required role")
}
}
}
}
def extractRoles(token: String): Directive1[Seq[String]] = {
token match {
case JsonWebToken(header, claimsSet, signature) => {
val decoded = decodeJWT(token)
decoded match {
case Some(_) => provide(extractRoleCodesFromJWT(decoded.get))
case None => provide(Seq())
}
}
case x =>
provide(Seq())
}
}
def validateToken(token: String): Directive0 = validate(validateJWT(token), INVALID_TOKEN_MESSAGE)
def extractToken: Directive1[Option[String]] = extractToken | extractHeader
def extractCookie: Directive1[Option[String]] = optionalCookie(JWT_COOKIE_NAME).map(_.map(_.value))
def extractHeader: Directive1[Option[String]] = optionalHeaderValueByName(JWT_HEADER_NAME)
All compiles fine, except the actual Directive0 that I want use later (authorize). The most inner line validate(...)
shows a compile error saying "Expression of type server.Directive0 doesn't conform to expected type server.Route"
How can I properly nest my other directives in order to form my authorize directive? Or can I concatenate them somehow else, instead of nesting? The documentation on custom directives is very slim unfortunately.
[UPDATE]
Thanks to the pointer from Java Anto this is what I came up with.
def authorize(role: String): Directive0 = {
extractToken.flatMap(validateToken)
.flatMap(extractRoles)
.flatMap(roles => validate(roles.contains(role), MISSING_ROLE_MESSAGE))
}
This compiles and will hopefully do the trick one I get to test it properly.