I have several endpoints already defined and I'm at the stage where I need to add in authorization. I have added a filter that extracts user permissions from the headers. I need to use this information to compare against the permissions that is unique to each route. I'm thinking I can do this by chaining the callback and with an Exception Filter.
Define an GET endpoint with Swagger documentation (see https://github.com/xiaodongw/swagger-finatra/blob/313702144bc6234823e80fb82cc7e9f8ba62966c/src/main/scala/com/twitter/finatra/http/SwaggerRouteDSL.scala):
def getWithDoc[RequestType: Manifest, ResponseType: Manifest](route: String, name: String = "", admin: Boolean = false, adminIndexInfo: Option[AdminIndexInfo] = None)
(doc: Operation => Unit)
(callback: RequestType => ResponseType): Unit = {
registerOperation(route, "get")(doc)
dsl.get(route, name, admin, adminIndexInfo)(callback)
}
An example usage:
getWithDoc("/api/hello") {
_.summary("Hello World")
} { request: HelloWorldRequest => // this request has information injected into it via filters
// stuff
}
I'd like to replace the above 'route' with MyRoute, like so:
sealed case class MyRoute(route: String, requiredPermissions: Set[String] = Set.empty)
// The only way I can think of to retrieve the request
trait FinagleHttpRequest {
def request: Request
}
case class HelloWorldRequest(@Inject override val request: Request) extends FinagleHttpRequest
case object ForbiddenRequest extends Exception
def authGetWithDoc[RequestType: Manifest, ResponseType: Manifest](route: MyRoute, name: String = "", admin: Boolean = false, adminIndexInfo: Option[AdminIndexInfo] = None)
(doc: Operation => Unit)
(callback: RequestType => ResponseType): Unit = {
val callbackWithAuth: RequestType => ResponseType = {
request: RequestType =>
val userPermissions = request.asInstanceOf[FinagleHttpRequest].request.userPermission // what was set by my filter
if isRequestAuthorized(userPermissions, route) {
callback(request)
} else {
// TODO Can't do response.forbidden because it's not of ResponseType
throw ForbiddenRequest // then use an exception filter to actually change the response to a forbidden
}
}
getWithDoc(route.route, name, admin, adminIndexInfo)(doc)(callbackWithAuth)
}
val HelloWorldRoute = MyRoute(route = "/api/hello", requiredPermissions = Set("foo", "bar"))
authGetWithDoc(HelloWorldRoute) {
_.summary("Hello World")
} { request: HelloWorldRequest =>
// stuff
}
This I think is the least invasive change and least amount of duplicated code.
I'm looking for some feedback on this approach and I'm open to any suggestions / improvements! Thanks!
I should probably wrap request.asInstanceOf[FinagleHttpRequest].request.userPermission in a Try to gracefully handle programmer forgetting to use the right RequestType
--
You received this message because you are subscribed to the Google Groups "finatra-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to finatra-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
On Sunday, November 5, 2017 at 8:23:07 AM UTC-8, Christopher Coco wrote:
> You probably want to look at using the Request Scope or the more recommended Request#ctx.
>
>
>
>
> Thanks!
> -c
>
>
> On Wed, Nov 1, 2017 at 2:59 PM, <stev...@exabeam.com> wrote:
> I should probably wrap request.asInstanceOf[FinagleHttpRequest].request.userPermission in a Try to gracefully handle programmer forgetting to use the right RequestType
>
>
>
>
>
> --
>
> You received this message because you are subscribed to the Google Groups "finatra-users" group.
>
> To unsubscribe from this group and stop receiving emails from it, send an email to finatra-user...@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to finatra-users+unsubscribe@googlegroups.com.
As endpoints is defined, I add some permission logic before the actual callback (see callbackWithAuth). This is to reduce copying the permission checking logic into all the endpoints. I couldn't figure out how to use only a filter since each route has different permissions.
When requests comes in:
"User" Filter -> Extracts information from header and sets it in Request's context - this is normal filter usage.
The "enhanced" callback is called which has permission checking and that happens. I throw an exception since I don't know anything about the ResponseType of the route. If permissions are valid then the original callback is called.
"Exception" filter catches the forbidden request and sends a 403
--
So I'm not extending the Request, I'm just requiring that any routes with permissions have to extend FinagleHttpRequest which has the request so I can actually use it to retrieve what was set in the user filter.