diff --git a/src/main/resources/sam.conf b/src/main/resources/sam.conf index 9fff7ddef..fc1e2a600 100644 --- a/src/main/resources/sam.conf +++ b/src/main/resources/sam.conf @@ -188,6 +188,7 @@ admin { azureServices { azureEnabled = ${?AZURE_ENABLED} + azureEnvironment = ${?AZURE_ENVIRONMENT} # defaults to AZURE allowManagedIdentityUserCreation = ${?AZURE_ALLOW_MANAGED_IDENTITY_USER_CREATION} managedAppServicePrincipal { clientId = ${?AZURE_MANAGED_APP_CLIENT_ID} diff --git a/src/main/scala/org/broadinstitute/dsde/workbench/sam/azure/CrlService.scala b/src/main/scala/org/broadinstitute/dsde/workbench/sam/azure/CrlService.scala index 6513ef98d..3580f60f7 100644 --- a/src/main/scala/org/broadinstitute/dsde/workbench/sam/azure/CrlService.scala +++ b/src/main/scala/org/broadinstitute/dsde/workbench/sam/azure/CrlService.scala @@ -5,13 +5,13 @@ import bio.terra.cloudres.common.ClientConfig import bio.terra.cloudres.common.cleanup.CleanupConfig import cats.effect.IO import com.azure.core.credential.TokenCredential -import com.azure.core.management.AzureEnvironment import com.azure.core.management.profile.AzureProfile import com.azure.identity.{ChainedTokenCredentialBuilder, ClientSecretCredentialBuilder, ManagedIdentityCredentialBuilder} import com.azure.resourcemanager.managedapplications.ApplicationManager import com.azure.resourcemanager.msi.MsiManager import com.azure.resourcemanager.resources.ResourceManager import com.google.auth.oauth2.ServiceAccountCredentials +import com.typesafe.scalalogging.LazyLogging import org.broadinstitute.dsde.workbench.sam.config.{AzureServicesConfig, JanitorConfig} import java.io.FileInputStream @@ -22,7 +22,7 @@ import scala.jdk.DurationConverters._ * * Note: this class is Azure-specific for now because Sam uses workbench-libs for Google Cloud calls. */ -class CrlService(config: AzureServicesConfig, janitorConfig: JanitorConfig) { +class CrlService(config: AzureServicesConfig, janitorConfig: JanitorConfig) extends LazyLogging { val clientId = "sam" val testResourceTimeToLive = 1 hour val clientConfigBase = ClientConfig.Builder.newBuilder().setClient("sam") @@ -72,6 +72,7 @@ class CrlService(config: AzureServicesConfig, janitorConfig: JanitorConfig) { config.managedAppServicePrincipal.foreach { servicePrincipalConfig => credential.addLast( new ClientSecretCredentialBuilder() + .authorityHost(config.azureEnvironment.getActiveDirectoryEndpoint) .clientId(servicePrincipalConfig.clientId) .clientSecret(servicePrincipalConfig.clientSecret) .tenantId(servicePrincipalConfig.tenantId) @@ -79,7 +80,7 @@ class CrlService(config: AzureServicesConfig, janitorConfig: JanitorConfig) { ) } - val profile = new AzureProfile(tenantId.value, subscriptionId.value, AzureEnvironment.AZURE) + val profile = new AzureProfile(tenantId.value, subscriptionId.value, config.azureEnvironment) (credential.build(), profile) } diff --git a/src/main/scala/org/broadinstitute/dsde/workbench/sam/config/AppConfig.scala b/src/main/scala/org/broadinstitute/dsde/workbench/sam/config/AppConfig.scala index 838e6c01e..9500fd3ef 100644 --- a/src/main/scala/org/broadinstitute/dsde/workbench/sam/config/AppConfig.scala +++ b/src/main/scala/org/broadinstitute/dsde/workbench/sam/config/AppConfig.scala @@ -1,6 +1,7 @@ package org.broadinstitute.dsde.workbench.sam.config import cats.data.NonEmptyList +import com.azure.core.management.AzureEnvironment import com.google.api.client.json.gson.GsonFactory import com.typesafe.config._ import net.ceedubs.ficus.Ficus._ @@ -196,6 +197,24 @@ object AppConfig { } yield AzureServicePrincipalConfig(clientId, clientSecret, tenantId) } + implicit val azureEnvironmentConfigReader: ValueReader[Option[AzureEnvironment]] = new ValueReader[Option[AzureEnvironment]] { + def read(config: Config, path: String): Option[AzureEnvironment] = + if (config.hasPath(path)) { + val azureEnvironment: String = config.getString(path) + val Azure: String = "AZURE" + val AzureGov: String = "AZURE_GOV" + + azureEnvironment match { + case AzureGov => Some(AzureEnvironment.AZURE_US_GOVERNMENT) + case Azure => Some(AzureEnvironment.AZURE) + case _ => throw new IllegalArgumentException(s"Unknown Azure environment: $azureEnvironment") + } + } else { + None + } + + } + implicit val azureServicesConfigReader: ValueReader[Option[AzureServicesConfig]] = ValueReader.relative { config => config .getAs[Boolean]("azureEnabled") @@ -207,7 +226,8 @@ object AppConfig { config.as[Option[AzureServicePrincipalConfig]]("managedAppServicePrincipal"), config.as[Option[AzureMarketPlace]]("azureMarketPlace"), config.as[Option[AzureServiceCatalog]]("azureServiceCatalog"), - config.as[Option[Boolean]]("allowManagedIdentityUserCreation").getOrElse(false) + config.as[Option[Boolean]]("allowManagedIdentityUserCreation").getOrElse(false), + config.as[Option[AzureEnvironment]]("azureEnvironment").getOrElse(AzureEnvironment.AZURE) ) ) } else { diff --git a/src/main/scala/org/broadinstitute/dsde/workbench/sam/config/AzureServicesConfig.scala b/src/main/scala/org/broadinstitute/dsde/workbench/sam/config/AzureServicesConfig.scala index 998d4a199..eb21f35eb 100644 --- a/src/main/scala/org/broadinstitute/dsde/workbench/sam/config/AzureServicesConfig.scala +++ b/src/main/scala/org/broadinstitute/dsde/workbench/sam/config/AzureServicesConfig.scala @@ -1,5 +1,7 @@ package org.broadinstitute.dsde.workbench.sam.config +import com.azure.core.management.AzureEnvironment + case class ManagedAppPlan(name: String, publisher: String, authorizedUserKey: String) case class AzureMarketPlace(managedAppPlans: Seq[ManagedAppPlan]) case class AzureServiceCatalog(authorizedUserKey: String, managedAppTypeServiceCatalog: String) @@ -8,10 +10,12 @@ case class AzureServicePrincipalConfig( clientSecret: String, tenantId: String ) + case class AzureServicesConfig( managedAppWorkloadClientId: Option[String], managedAppServicePrincipal: Option[AzureServicePrincipalConfig], azureMarketPlace: Option[AzureMarketPlace], azureServiceCatalog: Option[AzureServiceCatalog], - allowManagedIdentityUserCreation: Boolean + allowManagedIdentityUserCreation: Boolean, + azureEnvironment: AzureEnvironment ) {} diff --git a/src/test/scala/org/broadinstitute/dsde/workbench/sam/api/TestSamRoutes.scala b/src/test/scala/org/broadinstitute/dsde/workbench/sam/api/TestSamRoutes.scala index f57f5fddc..4634167a0 100644 --- a/src/test/scala/org/broadinstitute/dsde/workbench/sam/api/TestSamRoutes.scala +++ b/src/test/scala/org/broadinstitute/dsde/workbench/sam/api/TestSamRoutes.scala @@ -6,6 +6,7 @@ import akka.http.scaladsl.server.Directives.reject import akka.http.scaladsl.server.{Directive, Directive0} import akka.stream.Materializer import cats.effect.unsafe.implicits.global +import com.azure.core.management.AzureEnvironment import com.typesafe.config.ConfigFactory import org.broadinstitute.dsde.workbench.google.GoogleDirectoryDAO import org.broadinstitute.dsde.workbench.google.mock.MockGoogleDirectoryDAO @@ -224,7 +225,8 @@ object TestSamRoutes { Option(AzureServicePrincipalConfig("mock-managedapp-clientid", "mock-managedapp-clientsecret", "mock-managedapp-tenantid")), azureMarketPlace, azureServiceCatalog, - allowManagedIdentityUserCreation = true + allowManagedIdentityUserCreation = true, + azureEnvironment = AzureEnvironment.AZURE ) val azureService = diff --git a/src/test/scala/org/broadinstitute/dsde/workbench/sam/service/UserServiceSpecs/CreateUserSpec.scala b/src/test/scala/org/broadinstitute/dsde/workbench/sam/service/UserServiceSpecs/CreateUserSpec.scala index 47b6c1db2..4f1cc3e7b 100644 --- a/src/test/scala/org/broadinstitute/dsde/workbench/sam/service/UserServiceSpecs/CreateUserSpec.scala +++ b/src/test/scala/org/broadinstitute/dsde/workbench/sam/service/UserServiceSpecs/CreateUserSpec.scala @@ -1,5 +1,6 @@ package org.broadinstitute.dsde.workbench.sam.service.UserServiceSpecs +import com.azure.core.management.AzureEnvironment import org.broadinstitute.dsde.workbench.model._ import org.broadinstitute.dsde.workbench.sam.Generator.{genNewWorkbenchUserAzureUami, genWorkbenchUserAzure, genWorkbenchUserBoth, genWorkbenchUserGoogle} import org.broadinstitute.dsde.workbench.sam.config.AzureServicesConfig @@ -81,7 +82,7 @@ class CreateUserSpec extends UserServiceTestTraits { cloudExtensions, Seq.empty, defaultTosService, - Some(AzureServicesConfig(None, None, None, None, allowManagedIdentityUserCreation = true)) + Some(AzureServicesConfig(None, None, None, None, allowManagedIdentityUserCreation = true, azureEnvironment = AzureEnvironment.AZURE)) ) // Act @@ -295,7 +296,7 @@ class CreateUserSpec extends UserServiceTestTraits { cloudExtensions, Seq.empty, defaultTosService, - Some(AzureServicesConfig(None, None, None, None, allowManagedIdentityUserCreation = false)) + Some(AzureServicesConfig(None, None, None, None, allowManagedIdentityUserCreation = false, azureEnvironment = AzureEnvironment.AZURE)) ) // Act and Assert