-
Notifications
You must be signed in to change notification settings - Fork 4
CORE-308 Use okhttp rite #1570
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CORE-308 Use okhttp rite #1570
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport | |
import akka.http.scaladsl.model.{HttpRequest, StatusCode, StatusCodes} | ||
import akka.http.scaladsl.unmarshalling.Unmarshal | ||
import akka.stream.Materializer | ||
import okhttp3.Dispatcher | ||
import org.broadinstitute.dsde.firecloud.{FireCloudConfig, FireCloudExceptionWithErrorReport} | ||
import org.broadinstitute.dsde.firecloud.model.ErrorReportExtensions.FCErrorReport | ||
import org.broadinstitute.dsde.firecloud.model.ManagedGroupRoles.ManagedGroupRole | ||
|
@@ -27,11 +28,11 @@ import org.broadinstitute.dsde.firecloud.model.{ | |
WorkbenchUserInfo | ||
} | ||
import org.broadinstitute.dsde.firecloud.utils.RestJsonClient | ||
import org.broadinstitute.dsde.rawls.{RawlsException, RawlsExceptionWithErrorReport} | ||
import org.broadinstitute.dsde.rawls.RawlsException | ||
import org.broadinstitute.dsde.rawls.model.{ErrorReport, RawlsUserEmail, WorkspaceJsonSupport} | ||
import org.broadinstitute.dsde.workbench.client.sam.{ApiCallback, ApiClient, ApiException} | ||
import org.broadinstitute.dsde.workbench.client.sam.api.{ResourcesApi, UsersApi} | ||
import org.broadinstitute.dsde.workbench.client.sam.model.BulkMembershipUpdateRequestV2 | ||
import org.broadinstitute.dsde.workbench.client.sam.model.{BulkMembershipUpdateRequestV2, UserStatusInfo} | ||
import org.broadinstitute.dsde.workbench.model.WorkbenchIdentityJsonSupport._ | ||
import org.broadinstitute.dsde.workbench.model.google.GoogleProject | ||
import org.broadinstitute.dsde.workbench.model.{WorkbenchEmail, WorkbenchGroupName, WorkbenchUserId} | ||
|
@@ -57,6 +58,10 @@ class HttpSamDAO(implicit | |
with SprayJsonSupport { | ||
|
||
val timeout: FiniteDuration = 1.minute | ||
private val dispatcher = new Dispatcher() | ||
dispatcher.setMaxRequests(1000) | ||
dispatcher.setMaxRequestsPerHost(100) | ||
private val httpClient = new ApiClient().getHttpClient.newBuilder().dispatcher(dispatcher).build() | ||
|
||
override def listWorkspaceResources(implicit userInfo: WithAccessToken): Future[Seq[UserPolicy]] = | ||
authedRequestToObject[Seq[UserPolicy]](Get(samListResources("workspace")), | ||
|
@@ -214,8 +219,16 @@ class HttpSamDAO(implicit | |
callback.future.map(_ => ()) | ||
} | ||
|
||
override def getUserStatus(user: WithAccessToken): Future[UserStatusInfo] = { | ||
val apiClient = newApiClient(user) | ||
val sam = new UsersApi(apiClient) | ||
val callback = new SamApiCallback[UserStatusInfo]("getUserStatusInfo") | ||
sam.getUserStatusInfoAsync(callback) | ||
callback.future | ||
} | ||
|
||
private def newApiClient(user: WithAccessToken) = { | ||
val apiClient = new ApiClient() | ||
val apiClient = new ApiClient(httpClient) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the important change. |
||
apiClient.setAccessToken(user.accessToken.token) | ||
apiClient.setBasePath(FireCloudConfig.Sam.baseUrl) | ||
apiClient | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,8 @@ import akka.http.scaladsl.server.{RequestContext, Route, RouteResult} | |
import akka.http.scaladsl.unmarshalling.Unmarshal | ||
import akka.stream.Materializer | ||
import com.typesafe.scalalogging.LazyLogging | ||
import org.broadinstitute.dsde.firecloud.model.UserInfo | ||
import org.broadinstitute.dsde.firecloud.dataaccess.SamDAO | ||
import org.broadinstitute.dsde.firecloud.model.{UserInfo, WithAccessToken} | ||
import org.broadinstitute.dsde.firecloud.{FireCloudConfig, FireCloudExceptionWithErrorReport} | ||
import org.broadinstitute.dsde.rawls.model.{ErrorReport, ErrorReportSource} | ||
import org.broadinstitute.dsde.workbench.client.sam.api.UsersApi | ||
|
@@ -25,6 +26,8 @@ trait EnabledUserDirectives extends LazyLogging with SprayJsonSupport with Statu | |
implicit val executionContext: ExecutionContext | ||
implicit val materializer: Materializer | ||
|
||
val samDao: SamDAO | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changes in this file are related to moving the |
||
|
||
/** | ||
* Queries Sam to see if the current user is enabled. If the user is disabled, | ||
* responds with Forbidden and prevents the rest of the route from executing. | ||
|
@@ -33,18 +36,21 @@ trait EnabledUserDirectives extends LazyLogging with SprayJsonSupport with Statu | |
* @param samBaseUrl where to find Sam - used for unit testing | ||
* @return n/a | ||
*/ | ||
def requireEnabledUser(userInfo: UserInfo, samBaseUrl: String = FireCloudConfig.Sam.baseUrl)( | ||
def requireEnabledUser(userInfo: UserInfo)( | ||
innerRoute: RequestContext => Future[RouteResult] | ||
): Route = | ||
extractUri { uri => | ||
onComplete(getUserEnabled(userInfo.accessToken.token, samBaseUrl)) { | ||
onComplete(getUserEnabled(userInfo)) { | ||
case Success(true) => | ||
logger.debug(s"User ${userInfo.userEmail} is enabled: $uri") | ||
innerRoute | ||
case Success(false) => | ||
logger.warn(s"User ${userInfo.userEmail} is disabled: $uri") | ||
// the 401/"User is disabled." response mirrors what Sam returns in this case. | ||
throwErrorReport(StatusCodes.Unauthorized, "User is disabled.") | ||
case Failure(fcerr: FireCloudExceptionWithErrorReport) | ||
if fcerr.errorReport.statusCode.contains(StatusCodes.NotFound) => | ||
throwErrorReport(StatusCodes.Unauthorized, "User is not registered.") | ||
case Failure(fcerr: FireCloudExceptionWithErrorReport) => | ||
logger.error( | ||
s"FireCloudExceptionWithErrorReport exception checking enabled status for user ${userInfo.userEmail}: (${fcerr.getMessage}) while calling $uri", | ||
|
@@ -59,14 +65,10 @@ trait EnabledUserDirectives extends LazyLogging with SprayJsonSupport with Statu | |
apiex | ||
) | ||
val code = statusCodeFrom(apiex.getCode, Option(StatusCodes.InternalServerError)) | ||
if (code == StatusCodes.NotFound) { | ||
throwErrorReport(StatusCodes.Unauthorized, "User is not registered.") | ||
} else { | ||
val message = | ||
if (Option(apiex.getMessage).isEmpty || apiex.getMessage.isEmpty) code.defaultMessage() | ||
else apiex.getMessage | ||
throwErrorReport(code, message) | ||
} | ||
val message = | ||
if (Option(apiex.getMessage).isEmpty || apiex.getMessage.isEmpty) code.defaultMessage() | ||
else apiex.getMessage | ||
throwErrorReport(code, message) | ||
case Failure(ex) => | ||
logger.error( | ||
s"Unexpected exception checking enabled status for user ${userInfo.userEmail}: (${ex.getMessage}) while calling $uri", | ||
|
@@ -76,64 +78,8 @@ trait EnabledUserDirectives extends LazyLogging with SprayJsonSupport with Statu | |
} | ||
} | ||
|
||
private class SamApiCallback[T](functionName: String = "userStatusInfo") extends ApiCallback[T] { | ||
private val promise = Promise[T]() | ||
|
||
override def onFailure(e: ApiException, | ||
statusCode: Int, | ||
responseHeaders: java.util.Map[String, java.util.List[String]] | ||
): Unit = { | ||
val response = e.getResponseBody | ||
// attempt to propagate an ErrorReport from Sam. If we can't understand Sam's response as an ErrorReport, | ||
// create our own error message. | ||
import org.broadinstitute.dsde.rawls.model.WorkspaceJsonSupport.ErrorReportFormat | ||
toFutureTry(Unmarshal(response).to[ErrorReport]) flatMap { | ||
case Success(err) => | ||
logger.error(s"Sam call to $functionName failed with error $err") | ||
throw new FireCloudExceptionWithErrorReport(err) | ||
case Failure(_) => | ||
// attempt to extract something useful from the response entity, even though it's not an ErrorReport | ||
toFutureTry(Unmarshal(response).to[String]) map { maybeString => | ||
val stringErrMsg = maybeString match { | ||
case Success(stringErr) => stringErr | ||
case Failure(_) => response | ||
} | ||
throw new FireCloudExceptionWithErrorReport( | ||
ErrorReport(statusCodeFrom(statusCode, Option(StatusCodes.InternalServerError)), | ||
s"Sam call to $functionName failed with error '$stringErrMsg'" | ||
) | ||
) | ||
} | ||
} | ||
promise.failure(e) | ||
} | ||
override def onSuccess(result: T, | ||
statusCode: Int, | ||
responseHeaders: java.util.Map[String, java.util.List[String]] | ||
): Unit = promise.success(result) | ||
|
||
override def onUploadProgress(bytesWritten: Long, contentLength: Long, done: Boolean): Unit = () | ||
|
||
override def onDownloadProgress(bytesRead: Long, contentLength: Long, done: Boolean): Unit = () | ||
|
||
def future: Future[T] = promise.future | ||
} | ||
|
||
private def getUserEnabled(token: String, samBaseUrl: String): Future[Boolean] = { | ||
val apiClient = new ApiClient() | ||
apiClient.setAccessToken(token) | ||
apiClient.setBasePath(samBaseUrl) | ||
val sam = new UsersApi(apiClient) | ||
val callback = new SamApiCallback[UserStatusInfo] | ||
// TODO: should we use retries here? | ||
/* TODO: instead of directly calling Sam's userStatusInfo API, should we call a "real" api and, if that api returns | ||
401, echo its response? That would allow Orch to always give the same error response that Sam gives, but | ||
it is likely to be more expensive in the common case of the user being enabled. | ||
*/ | ||
sam.getUserStatusInfoAsync(callback) | ||
// TODO: is it enough to check this one `getEnabled` boolean? | ||
callback.future map (userStatus => Boolean.unbox(userStatus.getEnabled)) | ||
} | ||
private def getUserEnabled(user: WithAccessToken): Future[Boolean] = | ||
samDao.getUserStatus(user) map (userStatus => Boolean.unbox(userStatus.getEnabled)) | ||
|
||
/** | ||
* Constructs and throws a FireCloudExceptionWithErrorReport in response to the | ||
|
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am just guessing here. I will circle back to make these configurable if this ends up helping