Skip to content

Commit

Permalink
AJ-1438: disable most entity APIs for Azure workspaces (#2641)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidangb authored Dec 10, 2023
1 parent 672dd47 commit c6dff0e
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.broadinstitute.dsde.rawls.entities

import bio.terra.workspace.model.CloudPlatform
import org.broadinstitute.dsde.rawls.RawlsExceptionWithErrorReport
import org.broadinstitute.dsde.rawls.config.DataRepoEntityProviderConfig
import org.broadinstitute.dsde.rawls.dataaccess.datarepo.DataRepoDAO
Expand All @@ -9,7 +10,7 @@ import org.broadinstitute.dsde.rawls.entities.base.{EntityProvider, EntityProvid
import org.broadinstitute.dsde.rawls.entities.datarepo.{DataRepoEntityProvider, DataRepoEntityProviderBuilder}
import org.broadinstitute.dsde.rawls.entities.exceptions.DataEntityException
import org.broadinstitute.dsde.rawls.entities.local.{LocalEntityProvider, LocalEntityProviderBuilder}
import org.broadinstitute.dsde.rawls.model.ErrorReport
import org.broadinstitute.dsde.rawls.model.{ErrorReport, WorkspaceType}

import java.time.Duration
import scala.concurrent.{ExecutionContext, Future}
Expand Down Expand Up @@ -43,6 +44,12 @@ class EntityManager(providerBuilders: Set[EntityProviderBuilder[_ <: EntityProvi

def resolveProvider(requestArguments: EntityRequestArguments): Try[EntityProvider] = {

if (!WorkspaceType.RawlsWorkspace.equals(requestArguments.workspace.workspaceType)) {
throw new DataEntityException(
s"This API is disabled for ${CloudPlatform.AZURE} workspaces. Contact support for alternatives."
)
}

// soon: look up the reference name to ensure it exists.
// for now, this simplistic logic illustrates the approach: choose the right builder for the job.
val targetTag = if (requestArguments.dataReference.isDefined) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,44 +540,31 @@ class EntityService(protected val ctx: RawlsRequestContext,
}
}

def copyEntities(entityCopyDef: EntityCopyDefinition,
uri: Uri,
linkExistingEntities: Boolean
): Future[EntityCopyResponse] =
getV2WorkspaceContextAndPermissions(entityCopyDef.destinationWorkspace,
SamWorkspaceActions.write,
Some(WorkspaceAttributeSpecs(all = false))
) flatMap { destWorkspaceContext =>
getV2WorkspaceContextAndPermissions(entityCopyDef.sourceWorkspace,
SamWorkspaceActions.read,
Some(WorkspaceAttributeSpecs(all = false))
) flatMap { sourceWorkspaceContext =>
dataSource.inTransaction { dataAccess =>
for {
sourceAD <- DBIO.from(
samDAO.getResourceAuthDomain(SamResourceTypeNames.workspace, sourceWorkspaceContext.workspaceId, ctx)
)
destAD <- DBIO.from(
samDAO.getResourceAuthDomain(SamResourceTypeNames.workspace, destWorkspaceContext.workspaceId, ctx)
)
result <- authDomainCheck(sourceAD.toSet, destAD.toSet) flatMap { _ =>
val entityNames = entityCopyDef.entityNames
val entityType = entityCopyDef.entityType
val copyResults = traceDBIOWithParent("checkAndCopyEntities", ctx)(s1 =>
dataAccess.entityQuery.checkAndCopyEntities(sourceWorkspaceContext,
destWorkspaceContext,
entityType,
entityNames,
linkExistingEntities,
s1
)
)
copyResults
}
} yield result
}
}
}
def copyEntities(entityCopyDef: EntityCopyDefinition, linkExistingEntities: Boolean): Future[EntityCopyResponse] =
for {
destWsCtx <- getV2WorkspaceContextAndPermissions(entityCopyDef.destinationWorkspace,
SamWorkspaceActions.write,
Some(WorkspaceAttributeSpecs(all = false))
)
sourceWsCtx <- getV2WorkspaceContextAndPermissions(entityCopyDef.sourceWorkspace,
SamWorkspaceActions.read,
Some(WorkspaceAttributeSpecs(all = false))
)
sourceAD <- samDAO.getResourceAuthDomain(SamResourceTypeNames.workspace, sourceWsCtx.workspaceId, ctx)
destAD <- samDAO.getResourceAuthDomain(SamResourceTypeNames.workspace, destWsCtx.workspaceId, ctx)
_ = authDomainCheck(sourceAD.toSet, destAD.toSet)
entityRequestArguments = EntityRequestArguments(destWsCtx, ctx, None, None)
entityProvider <- entityManager.resolveProviderFuture(entityRequestArguments)
entityCopyResponse <- entityProvider
.copyEntities(sourceWsCtx,
destWsCtx,
entityCopyDef.entityType,
entityCopyDef.entityNames,
linkExistingEntities,
ctx
)
.recover(bigQueryRecover)
} yield entityCopyResponse

def batchUpdateEntitiesInternal(workspaceName: WorkspaceName,
entityUpdates: Seq[EntityUpdateDefinition],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import org.broadinstitute.dsde.rawls.model.{
AttributeEntityReference,
AttributeValue,
Entity,
EntityCopyDefinition,
EntityCopyResponse,
EntityQuery,
EntityQueryResponse,
EntityTypeMetadata,
RawlsRequestContext,
SubmissionValidationEntityInputs
SubmissionValidationEntityInputs,
Workspace
}

import scala.concurrent.Future
Expand Down Expand Up @@ -70,4 +73,12 @@ trait EntityProvider {
def batchUpdateEntities(entityUpdates: Seq[EntityUpdateDefinition]): Future[Traversable[Entity]]

def batchUpsertEntities(entityUpdates: Seq[EntityUpdateDefinition]): Future[Traversable[Entity]]

def copyEntities(sourceWorkspaceContext: Workspace,
destWorkspaceContext: Workspace,
entityType: String,
entityNames: Seq[String],
linkExistingEntities: Boolean,
parentContext: RawlsRequestContext
): Future[EntityCopyResponse]
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,16 @@ import org.broadinstitute.dsde.rawls.model.{
AttributeValueList,
AttributeValueRawJson,
Entity,
EntityCopyDefinition,
EntityCopyResponse,
EntityQuery,
EntityQueryResponse,
EntityTypeMetadata,
ErrorReport,
GoogleProjectId,
RawlsRequestContext,
SubmissionValidationEntityInputs
SubmissionValidationEntityInputs,
Workspace
}
import org.broadinstitute.dsde.rawls.util.CollectionUtils
import org.broadinstitute.dsde.rawls.{RawlsException, RawlsExceptionWithErrorReport}
Expand Down Expand Up @@ -524,4 +527,13 @@ class DataRepoEntityProvider(snapshotModel: SnapshotModel,

override def batchUpsertEntities(entityUpdates: Seq[EntityUpdateDefinition]): Future[Traversable[Entity]] =
throw new UnsupportedEntityOperationException("batch-upsert entities not supported by this provider.")

override def copyEntities(sourceWorkspaceContext: Workspace,
destWorkspaceContext: Workspace,
entityType: String,
entityNames: Seq[String],
linkExistingEntities: Boolean,
parentContext: RawlsRequestContext
): Future[EntityCopyResponse] =
throw new UnsupportedEntityOperationException("copy entities not supported by this provider.")
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,20 @@ import org.broadinstitute.dsde.rawls.model.{
AttributeEntityReference,
AttributeValue,
Entity,
EntityCopyDefinition,
EntityCopyResponse,
EntityQuery,
EntityQueryResponse,
EntityQueryResultMetadata,
EntityTypeMetadata,
ErrorReport,
RawlsRequestContext,
SamResourceTypeNames,
SamWorkspaceActions,
SubmissionValidationEntityInputs,
SubmissionValidationValue,
Workspace
Workspace,
WorkspaceAttributeSpecs
}
import org.broadinstitute.dsde.rawls.util.TracingUtils._
import org.broadinstitute.dsde.rawls.util.{AttributeSupport, CollectionUtils, EntitySupport}
Expand Down Expand Up @@ -404,4 +409,22 @@ class LocalEntityProvider(requestArguments: EntityRequestArguments,
override def batchUpsertEntities(entityUpdates: Seq[EntityUpdateDefinition]): Future[Traversable[Entity]] =
batchUpdateEntitiesImpl(entityUpdates, upsert = true)

override def copyEntities(sourceWorkspaceContext: Workspace,
destWorkspaceContext: Workspace,
entityType: String,
entityNames: Seq[String],
linkExistingEntities: Boolean,
parentContext: RawlsRequestContext
): Future[EntityCopyResponse] =
traceWithParent("checkAndCopyEntities", parentContext)(_ =>
dataSource.inTransaction { dataAccess =>
dataAccess.entityQuery.checkAndCopyEntities(sourceWorkspaceContext,
destWorkspaceContext,
entityType,
entityNames,
linkExistingEntities,
parentContext
)
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,14 @@ trait WorkspaceSupport {
} yield ()

// can't use withClonedAuthDomain because the Auth Domain -> no Auth Domain logic is different
def authDomainCheck(sourceWorkspaceADs: Set[String], destWorkspaceADs: Set[String]): ReadWriteAction[Boolean] =
def authDomainCheck(sourceWorkspaceADs: Set[String], destWorkspaceADs: Set[String]): Boolean =
// if the source has any auth domains, the dest must also *at least* have those auth domains
if (sourceWorkspaceADs.subsetOf(destWorkspaceADs)) DBIO.successful(true)
if (sourceWorkspaceADs.subsetOf(destWorkspaceADs)) true
else {
val missingGroups = sourceWorkspaceADs -- destWorkspaceADs
val errorMsg =
s"Source workspace has an Authorization Domain containing the groups ${missingGroups.mkString(", ")}, which are missing on the destination workspace"
DBIO.failed(new RawlsExceptionWithErrorReport(ErrorReport(StatusCodes.UnprocessableEntity, errorMsg)))
throw new RawlsExceptionWithErrorReport(ErrorReport(StatusCodes.UnprocessableEntity, errorMsg))
}

// WorkspaceContext helpers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ trait EntityApiService extends UserInfoDirectives {
entity(as[EntityCopyDefinition]) { copyDefinition =>
complete {
entityServiceConstructor(ctx)
.copyEntities(copyDefinition, request.uri, linkExistingEntitiesBool)
.copyEntities(copyDefinition, linkExistingEntitiesBool)
.map { response =>
if (
response.hardConflicts.isEmpty && (response.softConflicts.isEmpty || linkExistingEntitiesBool)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.broadinstitute.dsde.rawls.billing.BillingProjectOrchestrator
import org.broadinstitute.dsde.rawls.bucketMigration.BucketMigrationService
import org.broadinstitute.dsde.rawls.dataaccess.{ExecutionServiceCluster, SamDAO}
import org.broadinstitute.dsde.rawls.entities.EntityService
import org.broadinstitute.dsde.rawls.entities.exceptions.DataEntityException
import org.broadinstitute.dsde.rawls.genomics.GenomicsService
import org.broadinstitute.dsde.rawls.metrics.InstrumentationDirectives
import org.broadinstitute.dsde.rawls.model.{ApplicationVersion, ErrorReport, RawlsRequestContext, UserInfo}
Expand Down Expand Up @@ -62,6 +63,9 @@ object RawlsApiService extends LazyLogging {
Sentry.captureException(wsmApiException)
}
complete(wsmApiException.getCode -> ErrorReport(wsmApiException).copy(stackTrace = Seq()))
case dataEntityException: DataEntityException =>
// propagate only the message; don't include the stack trace
complete(dataEntityException.code -> ErrorReport(dataEntityException.getMessage))
case e: Throwable =>
// so we don't log the error twice when debug is enabled
if (logger.underlying.isDebugEnabled) {
Expand Down

0 comments on commit c6dff0e

Please sign in to comment.