Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ trait FireCloudApiService
with RegisterApiService
with WorkspaceApiService
with MethodConfigurationApiService
with SubmissionApiService
with StatusApiService
with MethodsApiService
with UserApiService
Expand All @@ -80,7 +79,6 @@ trait FireCloudApiService
with CromIamApiService
with HealthApiService
with StaticNotebooksApiService
with PerimeterApiService
with PassthroughApiService {

override lazy val log = LoggerFactory.getLogger(getClass)
Expand Down Expand Up @@ -179,11 +177,9 @@ trait FireCloudApiService
profileRoutes ~
cromIamApiServiceRoutes ~
methodConfigurationRoutes ~
submissionServiceRoutes ~
nihRoutes ~
shareLogServiceRoutes ~
staticNotebooksRoutes ~
perimeterServiceRoutes
staticNotebooksRoutes
}

val routeWrappers: Directive[Unit] =
Expand All @@ -195,7 +191,6 @@ trait FireCloudApiService

def route: server.Route = routeWrappers {
cromIamEngineRoutes ~
tosRoutes ~
exportEntitiesRoutes ~
cromIamEngineRoutes ~
exportEntitiesRoutes ~
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.broadinstitute.dsde.firecloud.service

import akka.http.scaladsl.client.RequestBuilding
import akka.http.scaladsl.model.headers.{`Content-Type`, Authorization}
import akka.http.scaladsl.model.{HttpMethod, Uri}
import akka.http.scaladsl.model.Uri
import akka.http.scaladsl.server.{Directives, Route}
import org.broadinstitute.dsde.firecloud.utils.RestJsonClient
import org.parboiled.common.FileUtils
Expand All @@ -20,51 +19,12 @@ object FireCloudDirectiveUtils {
}
toUri(path).toString
}

// TODO: should this pass through any other headers, such as Cookie?
/* This list controls which headers from the original request are passed through to the
* target service. It is important that some headers are NOT passed through:
* - if the Host header is passed through, it will not match the target service's host,
* and some servers - notably App Engine - will reject the request.
* - if headers that control the http protocol such as Accept-Encoding or Connection
* are passed through, they may not reflect reality. The original request may have
* come from a browser that supports different encodings or connection protocols
* than the service-to-service request we're about to make.
* - if headers that inform the target such as User-Agent, Referer or X-Forwarded-For
* are passed through, they will be misleading, as they reflect the original request
* and not the service-to-service request we're about to make.
*/
final val allowedPassthroughHeaders = List(Authorization, `Content-Type`).map(_.lowercaseName)

}

trait FireCloudDirectives extends Directives with RequestBuilding with RestJsonClient {

def passthrough(unencodedPath: String, methods: HttpMethod*): Route =
passthrough(Uri(unencodedPath), methods: _*)

// Danger: it is a common mistake to pass in a URI that omits the query parameters included in the original request to Orch.
// To preserve the query, extract it and attach it to the passthrough URI using `.withQuery(query)`.
def passthrough(uri: Uri, methods: HttpMethod*): Route = methods map { inMethod =>
generateExternalHttpRequestForMethod(uri, inMethod)
} reduce (_ ~ _)

def encodeUri(path: String): String = FireCloudDirectiveUtils.encodeUri(path)

private def generateExternalHttpRequestForMethod(uri: Uri, inMethod: HttpMethod) =
method(inMethod) { requestContext =>
val outgoingRequest = requestContext.request
.withUri(uri)
.withHeaders(
requestContext.request.headers.filter(hdr =>
FireCloudDirectiveUtils.allowedPassthroughHeaders.contains(hdr.lowercaseName())
)
)
requestContext.complete(
unAuthedRequest(outgoingRequest)
) // NOTE: This is actually AUTHED because we pass through the Authorization header
}

def withResourceFileContents(path: String)(innerRoute: String => Route): Route =
innerRoute(FileUtils.readAllTextFromResource(path))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ trait CromIamApiService
with StreamingPassthrough {

lazy val workflowRoot: String = FireCloudConfig.CromIAM.authUrl + "/workflows/v1"
lazy val womtoolRoute: String = FireCloudConfig.CromIAM.authUrl + "/womtool/v1"
lazy val engineRoot: String = FireCloudConfig.CromIAM.baseUrl + "/engine/v1"
lazy val rawlsWorkflowRoot: String = FireCloudConfig.Rawls.authUrl + "/workflows"

Expand Down Expand Up @@ -46,18 +45,7 @@ trait CromIamApiService
streamingPassthrough(Uri.Path(localBase) -> Uri(workflowRoot))
}

val womToolRoute: Route =
pathPrefix("womtool" / Segment) { _ =>
path("describe") {
pathEnd {
post {
passthrough(s"$womtoolRoute/describe", HttpMethods.POST)
}
}
}
}

val cromIamApiServiceRoutes = rawlsServiceRoute ~ cromIamServiceRoutes ~ womToolRoute
val cromIamApiServiceRoutes = rawlsServiceRoute ~ cromIamServiceRoutes

val cromIamEngineRoutes: Route =
pathPrefix("engine" / Segment) { _ =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import org.broadinstitute.dsde.firecloud.FireCloudConfig
import org.broadinstitute.dsde.firecloud.model.ModelJsonProtocol._
import org.broadinstitute.dsde.firecloud.model._
import org.broadinstitute.dsde.firecloud.service.FireCloudDirectives
import org.broadinstitute.dsde.firecloud.utils.{RestJsonClient, StandardUserInfoDirectives}
import org.broadinstitute.dsde.firecloud.utils.{RestJsonClient, StandardUserInfoDirectives, StreamingPassthrough}
import org.broadinstitute.dsde.rawls.model.WorkspaceName
import org.slf4j.LoggerFactory

Expand Down Expand Up @@ -93,100 +93,67 @@ trait MethodConfigurationApiService
extends FireCloudDirectives
with SprayJsonSupport
with StandardUserInfoDirectives
with StreamingPassthrough
with RestJsonClient {

final private val ApiPrefix = "workspaces"
lazy val log = LoggerFactory.getLogger(getClass)

val methodConfigurationRoutes: Route = requireUserInfo() { userInfo =>
path("template") {
passthrough(MethodConfigurationApiService.remoteTemplateURL, HttpMethods.POST)
} ~
path("inputsOutputs") {
passthrough(MethodConfigurationApiService.remoteInputsOutputsURL, HttpMethods.POST)
} ~
pathPrefix(ApiPrefix) {
pathPrefix(Segment / Segment / "method_configs") { (workspaceNamespace, workspaceName) =>
path("copyFromMethodRepo") {
post {
entity(as[CopyConfigurationIngest]) { ingest =>
val copyMethodConfig = new MethodConfigurationCopy(
methodRepoName = ingest.configurationName,
methodRepoNamespace = ingest.configurationNamespace,
methodRepoSnapshotId = ingest.configurationSnapshotId,
destination = Option(
MethodConfigurationId(
name = ingest.destinationName,
namespace = ingest.destinationNamespace,
workspaceName = Option(WorkspaceName(namespace = workspaceNamespace, name = workspaceName))
)
pathPrefix(ApiPrefix) {
pathPrefix(Segment / Segment / "method_configs") { (workspaceNamespace, workspaceName) =>
path("copyFromMethodRepo") {
post {
entity(as[CopyConfigurationIngest]) { ingest =>
val copyMethodConfig = new MethodConfigurationCopy(
methodRepoName = ingest.configurationName,
methodRepoNamespace = ingest.configurationNamespace,
methodRepoSnapshotId = ingest.configurationSnapshotId,
destination = Option(
MethodConfigurationId(
name = ingest.destinationName,
namespace = ingest.destinationNamespace,
workspaceName = Option(WorkspaceName(namespace = workspaceNamespace, name = workspaceName))
)
)
val extReq = Post(MethodConfigurationApiService.remoteCopyFromMethodRepoConfigUrl, copyMethodConfig)
)
val extReq = Post(MethodConfigurationApiService.remoteCopyFromMethodRepoConfigUrl, copyMethodConfig)

complete(userAuthedRequest(extReq)(userInfo))
}
complete(userAuthedRequest(extReq)(userInfo))
}
} ~ path("copyToMethodRepo") {
post {
entity(as[PublishConfigurationIngest]) { ingest =>
val copyMethodConfig = new MethodConfigurationPublish(
methodRepoName = ingest.configurationName,
methodRepoNamespace = ingest.configurationNamespace,
source = Option(
MethodConfigurationId(name = ingest.sourceName,
namespace = ingest.sourceNamespace,
workspaceName =
Option(WorkspaceName(namespace = workspaceNamespace, name = workspaceName))
)
}
} ~ path("copyToMethodRepo") {
post {
entity(as[PublishConfigurationIngest]) { ingest =>
val copyMethodConfig = new MethodConfigurationPublish(
methodRepoName = ingest.configurationName,
methodRepoNamespace = ingest.configurationNamespace,
source = Option(
MethodConfigurationId(name = ingest.sourceName,
namespace = ingest.sourceNamespace,
workspaceName =
Option(WorkspaceName(namespace = workspaceNamespace, name = workspaceName))
)
)
val extReq = Post(MethodConfigurationApiService.remoteCopyToMethodRepoConfigUrl, copyMethodConfig)
)
val extReq = Post(MethodConfigurationApiService.remoteCopyToMethodRepoConfigUrl, copyMethodConfig)

complete(userAuthedRequest(extReq)(userInfo))
}
complete(userAuthedRequest(extReq)(userInfo))
}
} ~ pathPrefix(Segment / Segment) { (configNamespace, configName) =>
pathEnd {
passthrough(
encodeUri(
MethodConfigurationApiService.remoteMethodConfigUrl(workspaceNamespace,
workspaceName,
configNamespace,
configName
)
),
HttpMethods.GET,
HttpMethods.PUT,
HttpMethods.POST,
HttpMethods.DELETE
)
} ~
path("rename") {
passthrough(encodeUri(
MethodConfigurationApiService.remoteMethodConfigRenameUrl(workspaceNamespace,
workspaceName,
configNamespace,
configName
)
),
HttpMethods.POST
)
} ~
path("validate") {
passthrough(encodeUri(
MethodConfigurationApiService.remoteMethodConfigValidateUrl(workspaceNamespace,
workspaceName,
configNamespace,
configName
)
),
HttpMethods.GET
)
}
}
} ~ pathPrefix(Segment / Segment) { (configNamespace, configName) =>
streamingPassthrough(
encodeUri(
MethodConfigurationApiService.remoteMethodConfigUrl(workspaceNamespace,
workspaceName,
configNamespace,
configName
)
)
)
}
}
}
}

}
Loading
Loading