Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to throw Exception from custom directive?

Tags:

spray

All API calls of my service are HTTP POSTs with parameters passed in multi-part body. Currently my authentication looks like this

formField("token".as[String]) { token =>
   authorize(isAuthorized(token)) {
      .... my other routes here
   }
}

But it looks too verbose. Ideally, I'd like to have something like:

myAuthorization {
   .... my other routes here
}

So I write it like this:

def myAuthorization: Directive0 = 
  formField("token".as[String]).require(isAuthorized(_))

But how would you write myAuthorization so that it throws AuthorizationFailedRejection with no token in a request?

like image 832
expert Avatar asked Jan 27 '26 15:01

expert


1 Answers

I like to write spray directives with extractors. Simplified example from one of my projects:

def loggedInUser: Directive[Option[User] :: HNil] = headerValue {
    case Cookie(cookies) => cookies.find(_.name === usrCookie) ∘ extractUserFromCookie
    case _ => None
} | reject(NoUserLoggedInRejection)

def authOpt(usrOpt: Option[User], usr: String): Directive0 =
    authorize(usrOpt.filter(_.login ≠ usr).isEmpty)

And then in your routing file you can use extractors:

pathPrefix("path") {
    loggedInUser { user =>
      authOpt(user, Clients.client) {
        path("path") {
          get {
            complete {
              html.page() // i'm using Twirl as a fe engine
            }
          }
        }
      }
    }

If no user is logged in then it throws a rejection wich is handled seperatly:

implicit val customRejectionHandlers = RejectionHandler {
  case NoUserLoggedInRejection :: _ =>
    ctx => ctx.redirect("/auth", StatusCodes.SeeOther)
}

That not the best in case of security example, but i think it's clear enough

Added

Example with headerValue directive from the comment:

// Lets define a extractor for the Host header from the request
val hostExtractor: HttpHeader => Option[String] = {
  case Host(host, port) => Some(host)
  case _ => None
}

// Then some path
val route = {
  path("somePath") {
    post {
      headerValue(hostExtractor) { host =>
        complete(host)
      }
    }
  }
}

Most of such directives are implemented through extract directive, which takes a function of type: RequestContext ⇒ T and returns T. For example headerValue also implemented through extract:

extract(_.request.headers.mapFind(protectedF)).flatMap {
  case Some(Right(a))        ⇒ provide(a)
  case Some(Left(rejection)) ⇒ reject(rejection)
  case None                  ⇒ reject
}
like image 155
4lex1v Avatar answered Jan 30 '26 08:01

4lex1v



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!