-
Notifications
You must be signed in to change notification settings - Fork 359
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* WX-696 Enable getting SAS token from WSM * Wire container resource id from config * Move resource-container-id config path * First pass at config for WSM * Remove unused singleton config * Tests for new config * Fix config parsing * Modified b2c token to be provided each time * Remove singletonConfig arg from factory * Restore types to factory configs * Clean up comments and empty token default * Default to config b2c before searching environment * Fix token default on api client * Fix test * Refactor error handling for when there is no token * Remove token constructor arg for clientProvider * Move configs to global singleton config * Update filesystems/blob/src/main/scala/cromwell/filesystems/blob/BlobFileSystemManager.scala * default -> override * Add override token to test * Update filesystems/blob/src/main/scala/cromwell/filesystems/blob/BlobFileSystemManager.scala Co-authored-by: Adam Nichols <anichols@broadinstitute.org> * Parentheses * Reduce token timeout * Move AzureCredentials to separate file * Make AzureCredentials an object * WSM token cleanup * Config refactor (#6960) Co-authored-by: Janet Gainer-Dewar <jdewar@broadinstitute.org> * Initial blob token documentation * Refine language in BlobSasTokenGenerator * Update comment and formatting Co-authored-by: Janet Gainer-Dewar <jdewar@broadinstitute.org> Co-authored-by: Adam Nichols <anichols@broadinstitute.org>
- Loading branch information
1 parent
2be0787
commit 46a7918
Showing
10 changed files
with
368 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
filesystems/blob/src/main/scala/cromwell/filesystems/blob/AzureCredentials.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package cromwell.filesystems.blob | ||
|
||
import cats.implicits.catsSyntaxValidatedId | ||
import com.azure.core.credential.TokenRequestContext | ||
import com.azure.core.management.AzureEnvironment | ||
import com.azure.core.management.profile.AzureProfile | ||
import com.azure.identity.DefaultAzureCredentialBuilder | ||
import common.validation.ErrorOr.ErrorOr | ||
|
||
import scala.concurrent.duration._ | ||
import scala.jdk.DurationConverters._ | ||
|
||
import scala.util.{Failure, Success, Try} | ||
|
||
/** | ||
* Strategy for obtaining an access token in an environment with available Azure identity. | ||
* If you need to disambiguate among multiple active user-assigned managed identities, pass | ||
* in the client id of the identity that should be used. | ||
*/ | ||
case object AzureCredentials { | ||
|
||
final val tokenAcquisitionTimeout = 5.seconds | ||
|
||
val azureProfile = new AzureProfile(AzureEnvironment.AZURE) | ||
val tokenScope = "https://management.azure.com/.default" | ||
|
||
private def tokenRequestContext: TokenRequestContext = { | ||
val trc = new TokenRequestContext() | ||
trc.addScopes(tokenScope) | ||
trc | ||
} | ||
|
||
private def defaultCredentialBuilder: DefaultAzureCredentialBuilder = | ||
new DefaultAzureCredentialBuilder() | ||
.authorityHost(azureProfile.getEnvironment.getActiveDirectoryEndpoint) | ||
|
||
def getAccessToken(identityClientId: Option[String]): ErrorOr[String] = { | ||
val credentials = identityClientId.foldLeft(defaultCredentialBuilder) { | ||
(builder, clientId) => builder.managedIdentityClientId(clientId) | ||
}.build() | ||
|
||
Try( | ||
credentials | ||
.getToken(tokenRequestContext) | ||
.block(tokenAcquisitionTimeout.toJava) | ||
) match { | ||
case Success(null) => "null token value attempting to obtain access token".invalidNel | ||
case Success(token) => token.getToken.validNel | ||
case Failure(error) => s"Failed to refresh access token: ${error.getMessage}".invalidNel | ||
} | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
filesystems/blob/src/main/scala/cromwell/filesystems/blob/BlobFileSystemConfig.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package cromwell.filesystems.blob | ||
|
||
import cats.implicits.catsSyntaxValidatedId | ||
import cats.syntax.apply._ | ||
import com.typesafe.config.Config | ||
import common.validation.Validation._ | ||
import net.ceedubs.ficus.Ficus._ | ||
|
||
import java.util.UUID | ||
|
||
// WSM config is needed for accessing WSM-managed blob containers created in Terra workspaces. | ||
// If the identity executing Cromwell has native access to the blob container, this can be ignored. | ||
final case class WorkspaceManagerConfig(url: WorkspaceManagerURL, | ||
workspaceId: WorkspaceId, | ||
containerResourceId: ContainerResourceId, | ||
overrideWsmAuthToken: Option[String]) // dev-only | ||
|
||
final case class BlobFileSystemConfig(endpointURL: EndpointURL, | ||
blobContainerName: BlobContainerName, | ||
subscriptionId: Option[SubscriptionId], | ||
expiryBufferMinutes: Long, | ||
workspaceManagerConfig: Option[WorkspaceManagerConfig]) | ||
|
||
object BlobFileSystemConfig { | ||
|
||
final val defaultExpiryBufferMinutes = 10L | ||
|
||
def apply(config: Config): BlobFileSystemConfig = { | ||
val endpointURL = parseString(config, "endpoint").map(EndpointURL) | ||
val blobContainer = parseString(config, "container").map(BlobContainerName) | ||
val subscriptionId = parseUUIDOpt(config, "subscription").map(_.map(SubscriptionId)) | ||
val expiryBufferMinutes = | ||
parseLongOpt(config, "expiry-buffer-minutes") | ||
.map(_.getOrElse(defaultExpiryBufferMinutes)) | ||
|
||
val wsmConfig = | ||
if (config.hasPath("workspace-manager")) { | ||
val wsmConf = config.getConfig("workspace-manager") | ||
val wsmURL = parseString(wsmConf, "url").map(WorkspaceManagerURL) | ||
val workspaceId = parseUUID(wsmConf, "workspace-id").map(WorkspaceId) | ||
val containerResourceId = parseUUID(wsmConf, "container-resource-id").map(ContainerResourceId) | ||
val overrideWsmAuthToken = parseStringOpt(wsmConf, "b2cToken") | ||
|
||
(wsmURL, workspaceId, containerResourceId, overrideWsmAuthToken) | ||
.mapN(WorkspaceManagerConfig) | ||
.map(Option(_)) | ||
} | ||
else None.validNel | ||
|
||
(endpointURL, blobContainer, subscriptionId, expiryBufferMinutes, wsmConfig) | ||
.mapN(BlobFileSystemConfig.apply) | ||
.unsafe("Couldn't parse blob filesystem config") | ||
} | ||
|
||
private def parseString(config: Config, path: String) = | ||
validate[String] { config.as[String](path) } | ||
|
||
private def parseStringOpt(config: Config, path: String) = | ||
validate[Option[String]] { config.as[Option[String]](path) } | ||
|
||
private def parseUUID(config: Config, path: String) = | ||
validate[UUID] { UUID.fromString(config.as[String](path)) } | ||
|
||
private def parseUUIDOpt(config: Config, path: String) = | ||
validate[Option[UUID]] { config.as[Option[String]](path).map(UUID.fromString) } | ||
|
||
private def parseLongOpt(config: Config, path: String) = | ||
validate[Option[Long]] { config.as[Option[Long]](path) } | ||
} | ||
|
||
// Our filesystem setup magic can't use BlobFileSystemConfig.apply directly, so we need this | ||
// wrapper class. | ||
class BlobFileSystemConfigWrapper(val config: BlobFileSystemConfig) { | ||
def this(config: Config) = this(BlobFileSystemConfig(config)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.