Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F76656960
AdminService.scala
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Fri, Aug 9, 15:55
Size
5 KB
Mime Type
text/x-c++
Expires
Sun, Aug 11, 15:55 (2 d)
Engine
blob
Format
Raw Data
Handle
19744045
Attached To
R2664 SHRINE MedCo Fork
AdminService.scala
View Options
package net.shrine.admin
import akka.actor.{ActorSystem, Actor}
import akka.event.Logging
import net.shrine.authentication.UserAuthenticator
import net.shrine.authorization.steward.OutboundUser
import net.shrine.i2b2.protocol.pm.User
import shapeless.HNil
import spray.http.{HttpResponse, HttpRequest, StatusCodes}
import spray.httpx.Json4sSupport
import spray.routing.directives.LogEntry
import spray.routing.{AuthenticationFailedRejection, Rejected, RouteConcatenation, Directive0, Route, HttpService}
import org.json4s.{DefaultFormats, Formats}
import scala.concurrent.ExecutionContext.Implicits.global
import net.shrine.admin.proxyto.ProxyDirectives.{proxyTo,proxyToUnmatchedPath}
// we don't implement our route structure directly in the service actor because
// we want to be able to test it independently, without having to spin up an actor
class AdminServiceActor extends Actor with AdminService {
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
def actorRefFactory = context
// this actor only runs our route, but you could add
// other things here, like request stream processing
// or timeout handling
def receive = runRoute(route)
}
// this trait defines our service behavior independently from the service actor
trait AdminService extends HttpService with Json4sSupport {
implicit def json4sFormats: Formats = DefaultFormats
val userAuthenticator = UserAuthenticator(AdminConfigSource.config)
//don't need to do anything special for unauthorized users, but they do need access to a static form.
lazy val route:Route = logRequestResponse(logEntryForRequestResponse _) { //logging is controlled by Akka's config, slf4j, and log4j config
gruntWatchCorsSupport{
staticResources ~ makeTrouble ~ about ~ authenticatedInBrowser
}
}
// logs just the request method, uri and response at info level
def logEntryForRequestResponse(req: HttpRequest): Any => Option[LogEntry] = {
case res: HttpResponse => {
Some(LogEntry(s"\n Request: $req \n Response: $res", Logging.InfoLevel))
}
case _ => None // other kind of responses
}
//pathPrefixTest shields the QEP code from the redirect.
def authenticatedInBrowser: Route = pathPrefixTest("user"|"admin") {
reportIfFailedToAuthenticate {
authenticate(userAuthenticator.basicUserAuthenticator) { user =>
pathPrefix("user") {userRoute(user)} ~
pathPrefix("admin") {adminRoute(user)}
}
}
}
val reportIfFailedToAuthenticate = routeRouteResponse {
case Rejected(List(AuthenticationFailedRejection(_,_))) =>
complete("AuthenticationFailed")
}
def makeTrouble = pathPrefix("makeTrouble") {
complete(throw new IllegalStateException("fake trouble"))
}
lazy val staticResources = pathPrefix("client"){
getFromResourceDirectory("client")
} ~ pathEnd {
redirect("shrine-admin/client/index.html", StatusCodes.PermanentRedirect) //todo pick up the top of the url from context instead of hard-coded "admin"
} ~ path( "index.html" ) {
redirect("client/index.html", StatusCodes.PermanentRedirect)
} ~ pathSingleSlash {
redirect("client/index.html", StatusCodes.PermanentRedirect)
}
lazy val about = pathPrefix("about") {
complete("Nothing here yet") //todo
}
def userRoute(user:User):Route = get {
pathPrefix("whoami") {
complete(OutboundUser.createFromUser(user))
}
}
//todo is this an admin? Does it matter?
def adminRoute(user:User):Route = get {
pathPrefix("happy") {
val happyHost = AdminConfigSource.config.getString("shrine.admin.happyHost")
val happyPort = AdminConfigSource.config.getString("shrine.admin.happyPort")
implicit val system = ActorSystem("sprayServer")
proxyToUnmatchedPath(s"http://$happyHost:$happyPort/happy")
}
}
}
//adapted from https://gist.github.com/joseraya/176821d856b43b1cfe19
object gruntWatchCorsSupport extends Directive0 with RouteConcatenation {
import spray.http.HttpHeaders.{`Access-Control-Allow-Methods`, `Access-Control-Max-Age`, `Access-Control-Allow-Headers`,`Access-Control-Allow-Origin`}
import spray.routing.directives.RespondWithDirectives.respondWithHeaders
import spray.routing.directives.MethodDirectives.options
import spray.routing.directives.RouteDirectives.complete
import spray.http.HttpMethods.{OPTIONS,GET,POST}
import spray.http.AllOrigins
private val allowOriginHeader = `Access-Control-Allow-Origin`(AllOrigins)
private val optionsCorsHeaders = List(
`Access-Control-Allow-Headers`("Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, Referer, User-Agent, Authorization"),
`Access-Control-Max-Age`(1728000)) //20 days
val gruntWatch:Boolean = AdminConfigSource.config.getBoolean("shrine.admin.gruntWatch")
override def happly(f: (HNil) => Route): Route = {
if(gruntWatch) {
options {
respondWithHeaders(`Access-Control-Allow-Methods`(OPTIONS, GET, POST) :: allowOriginHeader :: optionsCorsHeaders){
complete(StatusCodes.OK)
}
} ~ f(HNil)
}
else f(HNil)
}
}
Event Timeline
Log In to Comment