Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F98341053
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sun, Jan 12, 06:44
Size
9 KB
Mime Type
text/x-diff
Expires
Tue, Jan 14, 06:44 (23 h, 30 m)
Engine
blob
Format
Raw Data
Handle
23564810
Attached To
R2664 SHRINE MedCo Fork
View Options
diff --git a/apps/dashboard-app/src/main/resources/reference.conf b/apps/dashboard-app/src/main/resources/reference.conf
index a3d23eb4e..696a10239 100644
--- a/apps/dashboard-app/src/main/resources/reference.conf
+++ b/apps/dashboard-app/src/main/resources/reference.conf
@@ -1,48 +1,52 @@
shrine {
dashboard {
gruntWatch = false //false for production, true for mvn tomcat7:run . Allows the client javascript and html files to be loaded via gruntWatch .
happyBaseUrl = "http://localhost:6060/shrine/rest/happy"
+ remoteDashboard {
+ protocol = "https://"
+ port = 6443
+ }
}
pmEndpoint {
url = "http://changeme.com/i2b2/services/PMService/getServices" //"http://services.i2b2.org/i2b2/services/PMService/getServices"
acceptAllCerts = true
timeout {
seconds = 10
}
}
authenticate {
realm = "SHRINE Steward API"
usersource
{
type = "PmUserSource" //Must be ConfigUserSource (for isolated testing) or PmUserSource (for everything else)
domain = "set shrine.authenticate.usersource.domain to the PM authentication domain in dashboard.conf" //"i2b2demo"
}
}
// If the pmEndpoint acceptAllCerts = false then you need to supply a keystore
// Or if you would like dashboard-to-dashboard comms to work.
// keystore {
// file = "shrine.keystore"
// password = "chiptesting"
// privateKeyAlias = "test-cert"
// keyStoreType = "JKS"
// caCertAliases = [carra ca]
// }
}
//todo typesafe config precedence seems to do the right thing, but I haven't found the rules that say this reference.conf should override others
akka {
loglevel = INFO
// log-config-on-start = on
loggers = ["akka.event.slf4j.Slf4jLogger"]
// logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"
}
spray.servlet {
boot-class = "net.shrine.dashboard.Boot"
request-timeout = 30s
}
diff --git a/apps/dashboard-app/src/main/scala/net/shrine/dashboard/DashboardService.scala b/apps/dashboard-app/src/main/scala/net/shrine/dashboard/DashboardService.scala
index 660c0e5a0..4dae6d566 100644
--- a/apps/dashboard-app/src/main/scala/net/shrine/dashboard/DashboardService.scala
+++ b/apps/dashboard-app/src/main/scala/net/shrine/dashboard/DashboardService.scala
@@ -1,193 +1,197 @@
package net.shrine.dashboard
import akka.actor.{ActorSystem, Actor}
import akka.event.Logging
import net.shrine.authentication.UserAuthenticator
import net.shrine.authorization.steward.OutboundUser
import net.shrine.dashboard.jwtauth.ShrineJwtAuthenticator
import net.shrine.i2b2.protocol.pm.User
import net.shrine.dashboard.httpclient.HttpClientDirectives.{forwardUnmatchedPath,requestUriThenRoute}
import net.shrine.log.Loggable
import shapeless.HNil
import spray.http.{Uri, 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
// 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 DashboardServiceActor extends Actor with DashboardService {
// 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 DashboardService extends HttpService with Json4sSupport with Loggable {
implicit def json4sFormats: Formats = DefaultFormats
val userAuthenticator = UserAuthenticator(DashboardConfigSource.config)
//don't need to do anything special for unauthorized users, but they do need access to a static form.
lazy val route:Route = gruntWatchCorsSupport{
redirectToIndex ~ staticResources ~ makeTrouble ~ about ~ authenticatedInBrowser ~ authenticatedDashboard
}
// 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
}
// logs just the request method, uri and response status at info level
def logEntryForRequest(req: HttpRequest): Any => Option[LogEntry] = {
case res: HttpResponse => {
Some(LogEntry(s"\n Request: $req \n Response status: ${res.status}", Logging.InfoLevel))
}
case _ => None // other kind of responses
}
//pathPrefixTest shields the QEP code from the redirect.
def authenticatedInBrowser: Route = pathPrefixTest("user"|"admin") {
logRequestResponse(logEntryForRequestResponse _) { //logging is controlled by Akka's config, slf4j, and log4j config
reportIfFailedToAuthenticate {
authenticate(userAuthenticator.basicUserAuthenticator) { user =>
pathPrefix("user") {
userRoute(user)
} ~
pathPrefix("admin") {
adminRoute(user)
} ~
pathPrefix("toDashboard") {
toDashboardRoute(user)
}
}
}
}
}
val reportIfFailedToAuthenticate = routeRouteResponse {
case Rejected(List(AuthenticationFailedRejection(_,_))) =>
complete("AuthenticationFailed")
}
def authenticatedDashboard:Route = pathPrefix("fromDashboard") {
logRequestResponse(logEntryForRequestResponse _) { //logging is controlled by Akka's config, slf4j, and log4j config
get { //all remote dashboard calls are gets.
authenticate(ShrineJwtAuthenticator.authenticate) { user =>
adminRoute(user)
}
}
}
}
def makeTrouble = pathPrefix("makeTrouble") {
complete(throw new IllegalStateException("fake trouble"))
}
lazy val redirectToIndex = pathEnd {
redirect("shrine-dashboard/client/index.html", StatusCodes.PermanentRedirect) //todo pick up "shrine-dashboard" programatically
} ~
( path("index.html") | pathSingleSlash) {
redirect("client/index.html", StatusCodes.PermanentRedirect)
}
lazy val staticResources = pathPrefix("client") {
pathEnd {
redirect("client/index.html", StatusCodes.PermanentRedirect)
} ~
pathSingleSlash {
redirect("index.html", StatusCodes.PermanentRedirect)
} ~ {
getFromResourceDirectory("client")
}
}
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 {
implicit val system = ActorSystem("sprayServer")
pathPrefix("happy") {
val happyBaseUrl: String = DashboardConfigSource.config.getString("shrine.dashboard.happyBaseUrl")
forwardUnmatchedPath(happyBaseUrl)
} ~
pathPrefix("messWithHappyVersion") {
val happyBaseUrl: String = DashboardConfigSource.config.getString("shrine.dashboard.happyBaseUrl")
def pullClasspathFromConfig(httpResponse:HttpResponse,uri:Uri):Route = {
ctx => {
val result = httpResponse.entity.asString
ctx.complete(s"Got '$result' from $uri")
}
}
requestUriThenRoute(happyBaseUrl+"/version",pullClasspathFromConfig)
} ~
pathPrefix("ping") {complete("pong")} //ping test
}
def toDashboardRoute(user:User):Route = get {
implicit val system = ActorSystem("sprayServer")
- val baseUrl = "localhost" //todo from a param??
+ pathPrefix(Segment) { dnsName =>
+ val remoteDashboardProtocol = DashboardConfigSource.config.getString("shrine.dashboard.remoteDashboard.protocol")
+ val remoteDashboardPort = DashboardConfigSource.config.getString("shrine.dashboard.remoteDashboard.port")
- forwardUnmatchedPath(baseUrl)
- }
+ val baseUrl = s"$remoteDashboardProtocol$dnsName:$remoteDashboardPort"
+ forwardUnmatchedPath(baseUrl,Some(ShrineJwtAuthenticator.createAuthHeader))
+ }
+ }
}
//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 = DashboardConfigSource.config.getBoolean("shrine.dashboard.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