Skip to content
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

ID-1301 talk to ecm for nih username (Round 2) #1407

Merged
merged 31 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
58d4922
ID-1301 Talk to ECM for NIH Account Status
tlangs Jul 19, 2024
73fa847
tests pass
tlangs Jul 26, 2024
f31ae71
new NihService tests
tlangs Jul 30, 2024
94f0f0b
fix sam test
tlangs Jul 30, 2024
4631e01
exclude spring stuff
tlangs Jul 30, 2024
0649477
Merge branch 'develop' into tl_ID-1301_talk_to_ecm_for_nih_username
tlangs Jul 30, 2024
62a3f16
orch spins up locally
tlangs Jul 31, 2024
08bc3f2
ecm isn't optional
tlangs Jul 31, 2024
8b18883
remove test conf
tlangs Jul 31, 2024
ca9e839
ID-1301 Support disabling ECM for swat tests
tlangs Aug 1, 2024
361c9f2
cure runtime exception
tlangs Aug 1, 2024
5c5598b
info logging
tlangs Aug 1, 2024
10882f5
behavior confirmed, logging no longer needed
tlangs Aug 1, 2024
daf388e
re-add info logging
tlangs Aug 2, 2024
12b4aee
warn is more appropriate
tlangs Aug 2, 2024
bda6d6d
Merge branch 'develop' into tl_ID-1301_talk_to_ecm_for_nih_username
tlangs Aug 2, 2024
94a105c
maybe listing a users groups takes too long
tlangs Aug 5, 2024
2cfcb16
Merge branch 'develop' into tl_ID-1301_talk_to_ecm_for_nih_username
tlangs Aug 5, 2024
77ea5a9
listing groups isn't the problem
tlangs Aug 6, 2024
5679cec
Merge branch 'tl_ID-1301_talk_to_ecm_for_nih_username' of github.com:…
tlangs Aug 6, 2024
c9e293c
more dependency issues
tlangs Aug 6, 2024
3c06bfe
stop changing things, dude
tlangs Aug 6, 2024
c7cc114
error handling
tlangs Aug 6, 2024
fb57e2c
better error handling code
tlangs Aug 6, 2024
668b525
more improvements
tlangs Aug 6, 2024
d896e7d
another test case
tlangs Aug 6, 2024
3cd1230
correct sliding
tlangs Aug 6, 2024
4aa9735
back to the old way
tlangs Aug 6, 2024
d6b0555
return all groups from sam and fix local run
tlangs Aug 7, 2024
d07a609
a bunch of changes based on manual testing
tlangs Aug 7, 2024
fe7fdbd
don't need that import anymore
tlangs Aug 8, 2024
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
2 changes: 1 addition & 1 deletion local-dev/templates/docker-rsync-local-orch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ start_server () {
--network=fc-orch \
-e JAVA_OPTS="$DOCKER_JAVA_OPTS" \
sbtscala/scala-sbt:eclipse-temurin-jammy-17.0.10_7_1.10.1_2.13.14 \
sbt \~reStart
bash -c "git config --global --add safe.directory /app && sbt \~reStart"
Copy link
Contributor Author

@tlangs tlangs Aug 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes a

[info] loading settings for project root from build.sbt ...
fatal: detected dubious ownership in repository at '/app'
To add an exception for this directory, call:

        git config --global --add safe.directory /app

error


docker cp config/firecloud-account.pem orch-sbt:/etc/firecloud-account.pem
docker cp config/firecloud-account.json orch-sbt:/etc/firecloud-account.json
Expand Down
5 changes: 5 additions & 0 deletions local-dev/templates/firecloud-orchestration.conf.ctmpl
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ cwds {
bucketName = "cwds-batchupsert-dev"
}

externalCreds {
baseUrl = "https://externalcreds.dsde-dev.broadinstitute.org"
enabled = true
}

firecloud {
baseUrl = "https://firecloud-orchestration.dsde-dev.broadinstitute.org"
portalUrl = "https://firecloud.dsde-dev.broadinstitute.org"
Expand Down
8 changes: 7 additions & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ object Dependencies {
val excludeAkkaHttp = ExclusionRule(organization = "com.typesafe.akka", name = "akka-http_2.13")
val excludeSprayJson = ExclusionRule(organization = "com.typesafe.akka", name = "akka-http-spray-json_2.13")

val excludeSpring = ExclusionRule(organization = "org.springframework")
val excludeSpringBoot = ExclusionRule(organization = "org.springframework.boot")
val excludeSpringJcl = ExclusionRule(organization = "org.springframework", name = "spring-jcl")
Comment on lines +17 to +19
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exclusions to resolve assembly merge clashes, and also to reduce the jar size


// Overrides for transitive dependencies. These apply - via Settings.scala - to all projects in this codebase.
// These are overrides only; if the direct dependencies stop including any of these, they will not be included
// by being listed here.
Expand Down Expand Up @@ -50,9 +54,11 @@ object Dependencies {
excludeGuava("org.broadinstitute.dsde.workbench" %% "workbench-util" % s"0.10-$workbenchLibsHash"),
"org.broadinstitute.dsde.workbench" %% "workbench-google2" % s"0.36-$workbenchLibsHash",
"org.broadinstitute.dsde.workbench" %% "workbench-oauth2" % s"0.7-$workbenchLibsHash",
"org.broadinstitute.dsde.workbench" %% "sam-client" % "0.1-ef83073",
"org.broadinstitute.dsde.workbench" %% "sam-client" % "v0.0.263",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was ancient!

"org.broadinstitute.dsde.workbench" %% "workbench-notifications" %s"0.6-$workbenchLibsHash",
"org.databiosphere" % "workspacedataservice-client-okhttp-jakarta" % "0.2.167-SNAPSHOT",
"bio.terra" % "externalcreds-client-resttemplate" % "1.44.0-20240725.201427-1" excludeAll(excludeSpring, excludeSpringBoot),
"org.springframework" % "spring-web" % "6.1.11" excludeAll(excludeSpringBoot, excludeSpringJcl),

"com.typesafe.akka" %% "akka-actor" % akkaV,
"com.typesafe.akka" %% "akka-slf4j" % akkaV,
Expand Down
1 change: 1 addition & 0 deletions project/Merging.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ object Merging {
// Error: /home/sbtuser/.cache/coursier/v1/https/repo1.maven.org/maven2/com/typesafe/akka/akka-protobuf-v3_2.13/2.6.19/akka-protobuf-v3_2.13-2.6.19.jar:google/protobuf/struct.proto
case PathList("google", "protobuf", _ @ _*) => MergeStrategy.first
case PathList("META-INF", "versions", "9", "OSGI-INF", "MANIFEST.MF") => MergeStrategy.first
case PathList("META-INF", "spring", "aot.factories") => MergeStrategy.first
case x => oldStrategy(x)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ case class Application(agoraDAO: AgoraDAO,
thurloeDAO: ThurloeDAO,
shareLogDAO: ShareLogDAO,
shibbolethDAO: ShibbolethDAO,
cwdsDAO: CwdsDAO)
cwdsDAO: CwdsDAO,
ecmDAO: ExternalCredsDAO)
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ object Boot extends App with LazyLogging {
val rawlsDAO: RawlsDAO = new HttpRawlsDAO
val samDAO: SamDAO = new HttpSamDAO
val thurloeDAO: ThurloeDAO = new HttpThurloeDAO
val ecmDAO: ExternalCredsDAO = if (FireCloudConfig.ExternalCreds.enabled) new HttpExternalCredsDAO else new DisabledExternalCredsDAO

// can be disabled
val agoraDAO: AgoraDAO = whenEnabled[AgoraDAO](FireCloudConfig.Agora.enabled, new HttpAgoraDAO(FireCloudConfig.Agora))
Expand All @@ -110,7 +111,7 @@ object Boot extends App with LazyLogging {
val searchDAO: SearchDAO = elasticSearchClient.map(new ElasticSearchDAO(_, FireCloudConfig.ElasticSearch.indexName, researchPurposeSupport)).getOrElse(DisabledServiceFactory.newDisabledService[SearchDAO])
val shareLogDAO: ShareLogDAO = elasticSearchClient.map(new ElasticSearchShareLogDAO(_, FireCloudConfig.ElasticSearch.shareLogIndexName)).getOrElse(DisabledServiceFactory.newDisabledService[ShareLogDAO])

Application(agoraDAO, googleServicesDAO, ontologyDAO, rawlsDAO, samDAO, searchDAO, researchPurposeSupport, thurloeDAO, shareLogDAO, shibbolethDAO, cwdsDAO)
Application(agoraDAO, googleServicesDAO, ontologyDAO, rawlsDAO, samDAO, searchDAO, researchPurposeSupport, thurloeDAO, shareLogDAO, shibbolethDAO, cwdsDAO, ecmDAO)
}

private def whenEnabled[T : ClassTag](enabled: Boolean, realService: => T): T = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.broadinstitute.dsde.firecloud
import akka.http.scaladsl.model.Uri
import akka.http.scaladsl.model.Uri.{Authority, Host, Query}
import com.typesafe.config.{Config, ConfigFactory, ConfigObject}
import org.broadinstitute.dsde.firecloud.service.{FireCloudDirectiveUtils, NihWhitelist}
import org.broadinstitute.dsde.firecloud.service.{FireCloudDirectiveUtils, NihAllowlist}
import org.broadinstitute.dsde.rawls.model.{EntityQuery, SortDirections}
import org.broadinstitute.dsde.workbench.model.WorkbenchGroupName

Expand Down Expand Up @@ -125,6 +125,13 @@ object FireCloudConfig {
lazy val bucket: String = cwds.getString("bucketName")
}

object ExternalCreds {
// lazy - only required when External Credentials is enabled
private lazy val externalCreds = config.getConfig("externalCreds")
lazy val baseUrl: String = externalCreds.getString("baseUrl")
lazy val enabled: Boolean = externalCreds.getBoolean("enabled")
}

object FireCloud {
private val firecloud = config.getConfig("firecloud")
val fireCloudId = firecloud.getString("fireCloudId")
Expand All @@ -147,15 +154,15 @@ object FireCloudConfig {
// lazy - only required when nih is enabled
private lazy val nih = config.getConfig("nih")
lazy val whitelistBucket = nih.getString("whitelistBucket")
lazy val whitelists: Set[NihWhitelist] = {
lazy val whitelists: Set[NihAllowlist] = {
val whitelistConfigs = nih.getConfig("whitelists")

whitelistConfigs.root.asScala.collect { case (name, configObject:ConfigObject) =>
val config = configObject.toConfig
val rawlsGroup = config.getString("rawlsGroup")
val fileName = config.getString("fileName")

NihWhitelist(name, WorkbenchGroupName(rawlsGroup), fileName)
NihAllowlist(name, WorkbenchGroupName(rawlsGroup), fileName)
}
}.toSet
val enabled = nih.optionalBoolean("enabled").getOrElse(true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.broadinstitute.dsde.firecloud.dataaccess

import com.typesafe.scalalogging.LazyLogging
import org.broadinstitute.dsde.firecloud.model.{LinkedEraAccount, UserInfo, WithAccessToken}

import scala.concurrent.Future

class DisabledExternalCredsDAO extends ExternalCredsDAO with LazyLogging {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed so that Orch can run in BEEs. In the future, we'll need to get ECM to run in BEEs so that swat tests can pass without Thurloe.


override def getLinkedAccount(implicit userInfo: UserInfo): Future[Option[LinkedEraAccount]] = Future.successful {
logger.warn("Getting Linked eRA Account from ECM, but ECM is disabled.")
None
}

override def putLinkedEraAccount(linkedEraAccount: LinkedEraAccount)(implicit orchInfo: WithAccessToken): Future[Unit] = Future.successful {
logger.warn("Putting Linked eRA Account to ECM, but ECM is disabled.")
}

override def deleteLinkedEraAccount(userInfo: UserInfo)(implicit orchInfo: WithAccessToken): Future[Unit] = Future.successful {
logger.warn("Deleting Linked eRA Account from ECM, but ECM is disabled.")
}

override def getLinkedEraAccountForUsername(username: String)(implicit orchInfo: WithAccessToken): Future[Option[LinkedEraAccount]] = Future.successful {
logger.warn("Getting Linked eRA Account for username from ECM, but ECM is disabled.")
None
}

override def getActiveLinkedEraAccounts(implicit orchInfo: WithAccessToken): Future[Seq[LinkedEraAccount]] = Future.successful {
logger.warn("Getting Active Linked eRA Accounts from ECM, but ECM is disabled.")
Seq.empty
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.broadinstitute.dsde.firecloud.dataaccess

import org.broadinstitute.dsde.firecloud.model.{LinkedEraAccount, UserInfo, WithAccessToken}
import org.databiosphere.workspacedata.client.ApiException

import scala.concurrent.Future

trait ExternalCredsDAO {

@throws(classOf[ApiException])
def getLinkedAccount(implicit userInfo: UserInfo): Future[Option[LinkedEraAccount]]

@throws(classOf[ApiException])
def putLinkedEraAccount(linkedEraAccount: LinkedEraAccount)(implicit orchInfo: WithAccessToken): Future[Unit]

@throws(classOf[ApiException])
def deleteLinkedEraAccount(userInfo: UserInfo)(implicit orchInfo: WithAccessToken): Future[Unit]

@throws(classOf[ApiException])
def getLinkedEraAccountForUsername(username: String)(implicit orchInfo: WithAccessToken): Future[Option[LinkedEraAccount]]

@throws(classOf[ApiException])
def getActiveLinkedEraAccounts(implicit orchInfo: WithAccessToken): Future[Seq[LinkedEraAccount]]

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package org.broadinstitute.dsde.firecloud.dataaccess

import bio.terra.externalcreds.api.OauthApi
import bio.terra.externalcreds.api.AdminApi
import bio.terra.externalcreds.client.ApiClient
import bio.terra.externalcreds.model.Provider
import com.google.api.client.http.HttpStatusCodes
import org.broadinstitute.dsde.firecloud.FireCloudConfig
import org.broadinstitute.dsde.firecloud.model.LinkedEraAccount.unapply
import org.broadinstitute.dsde.firecloud.model.{LinkedEraAccount, UserInfo, WithAccessToken}
import org.broadinstitute.dsde.workbench.model.WorkbenchException
import org.joda.time.DateTime
import org.springframework.web.client.{HttpClientErrorException, RestTemplate}

import scala.concurrent.{ExecutionContext, Future}
import scala.jdk.CollectionConverters._

class HttpExternalCredsDAO(implicit val executionContext: ExecutionContext) extends ExternalCredsDAO {

private lazy val restTemplate = new RestTemplate

private def handleError[A](e: HttpClientErrorException, operation: String): Option[A] = {
e.getStatusCode.value() match {
case HttpStatusCodes.STATUS_CODE_NOT_FOUND => None
case _ => throw new WorkbenchException(s"Failed to $operation: ${e.getMessage}")
}
}

override def getLinkedAccount(implicit userInfo: UserInfo): Future[Option[LinkedEraAccount]] = Future {
val oauthApi: OauthApi = getOauthApi(userInfo.accessToken.token)
try {
val linkInfo = oauthApi.getLink(Provider.ERA_COMMONS)
Some(LinkedEraAccount(userInfo.id, linkInfo.getExternalUserId, new DateTime(linkInfo.getExpirationTimestamp)))
} catch {
case e: HttpClientErrorException => handleError(e, "GET eRA Linked Account")
}
}

override def putLinkedEraAccount(linkedEraAccount: LinkedEraAccount)(implicit orchInfo: WithAccessToken): Future[Unit] = Future {
val adminApi = getAdminApi(orchInfo.accessToken.token)
adminApi.putLinkedAccountWithFakeToken(unapply(linkedEraAccount), Provider.ERA_COMMONS)
}

override def deleteLinkedEraAccount(userInfo: UserInfo)(implicit orchInfo: WithAccessToken): Future[Unit] = Future {
val adminApi = getAdminApi(orchInfo.accessToken.token)
try {
adminApi.adminDeleteLinkedAccount(userInfo.id, Provider.ERA_COMMONS)
} catch {
case e: HttpClientErrorException => handleError(e, "DELETE eRA Linked Account")
}
}

override def getLinkedEraAccountForUsername(username: String)(implicit orchInfo: WithAccessToken): Future[Option[LinkedEraAccount]] = Future {
val adminApi = getAdminApi(orchInfo.accessToken.token)
try {
val adminLinkInfo = adminApi.getLinkedAccountForExternalId(Provider.ERA_COMMONS, username)
Some(LinkedEraAccount(adminLinkInfo))
} catch {
case e: HttpClientErrorException => handleError(e, s"GET eRA Linked Account for username [$username]")
}
}

override def getActiveLinkedEraAccounts(implicit orchInfo: WithAccessToken): Future[Seq[LinkedEraAccount]] = Future {
val adminApi = getAdminApi(orchInfo.accessToken.token)
val adminLinkInfos = adminApi.getActiveLinkedAccounts(Provider.ERA_COMMONS)
adminLinkInfos.asScala.map(LinkedEraAccount.apply).toSeq
}

private def getApi(accessToken: String): ApiClient = {
val client = new ApiClient(restTemplate)
client.setBasePath(FireCloudConfig.ExternalCreds.baseUrl)
client.setAccessToken(accessToken)
client
}

private def getOauthApi(accessToken: String): OauthApi = {
val client = getApi(accessToken)
new OauthApi(client)
}

private def getAdminApi(accessToken: String): AdminApi = {
val client = getApi(accessToken)
new AdminApi(client)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import org.broadinstitute.dsde.firecloud.model.ErrorReportExtensions.FCErrorRepo
import org.broadinstitute.dsde.firecloud.model.ManagedGroupRoles.ManagedGroupRole
import org.broadinstitute.dsde.firecloud.model.ModelJsonProtocol._
import org.broadinstitute.dsde.firecloud.model.SamResource.UserPolicy
import org.broadinstitute.dsde.firecloud.model.{AccessToken, FireCloudManagedGroupMembership, ManagedGroupRoles, RegistrationInfo, RegistrationInfoV2, SamUserAttributesRequest, SamUserRegistrationRequest, SamUserResponse, UserIdInfo, UserInfo, WithAccessToken}
import org.broadinstitute.dsde.firecloud.model.{AccessToken, FireCloudManagedGroupMembership, ManagedGroupRoles, RegistrationInfo, SamUser, SamUserAttributesRequest, SamUserRegistrationRequest, SamUserResponse, UserIdInfo, UserInfo, WithAccessToken, WorkbenchUserInfo}
import org.broadinstitute.dsde.firecloud.utils.RestJsonClient
import org.broadinstitute.dsde.rawls.model.RawlsUserEmail
import org.broadinstitute.dsde.workbench.model.WorkbenchIdentityJsonSupport._
import org.broadinstitute.dsde.workbench.model.google.GoogleProject
import org.broadinstitute.dsde.workbench.model.{WorkbenchEmail, WorkbenchGroupName}
import org.broadinstitute.dsde.workbench.model.{WorkbenchEmail, WorkbenchGroupName, WorkbenchUserId}
import org.broadinstitute.dsde.workbench.util.health.SubsystemStatus
import spray.json.DefaultJsonProtocol._
import spray.json.{JsValue, JsonFormat, RootJsonFormat}
Expand Down Expand Up @@ -50,6 +50,14 @@ class HttpSamDAO( implicit val system: ActorSystem, val materializer: Materializ
authedRequestToObject[UserIdInfo](Get(samGetUserIdsUrl.format(URLEncoder.encode(email.value, UTF_8.name))))
}

// Sam's API only allows for 1000 user to be fetched at one time
override def getUsersForIds(samUserIds: Seq[WorkbenchUserId])(implicit userInfo: WithAccessToken): Future[Seq[WorkbenchUserInfo]] = Future.sequence {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New Sam API to resolve a list of users from user ids.

samUserIds.sliding(1000, 1000).toSeq.map { batch =>
adminAuthedRequestToObject[Seq[SamUser]](Post(samAdminGetUsersForIdsUrl, batch))
.map(_.map(user => WorkbenchUserInfo(user.id.value, user.email.value)))
}
}.map(_.flatten)

override def isGroupMember(groupName: WorkbenchGroupName, userInfo: UserInfo): Future[Boolean] = {
implicit val accessToken = userInfo
authedRequestToObject[List[String]](Get(samResourceRoles(managedGroupResourceTypeName, groupName.value)), label=Some("HttpSamDAO.isGroupMember")).map { allRoles =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import com.typesafe.scalalogging.LazyLogging
import org.broadinstitute.dsde.firecloud.FireCloudConfig
import org.broadinstitute.dsde.firecloud.model.ManagedGroupRoles.ManagedGroupRole
import org.broadinstitute.dsde.firecloud.model.SamResource.UserPolicy
import org.broadinstitute.dsde.firecloud.model.{AccessToken, FireCloudManagedGroupMembership, RegistrationInfo, RegistrationInfoV2, SamUserResponse, UserIdInfo, UserInfo, WithAccessToken}
import org.broadinstitute.dsde.firecloud.model.{AccessToken, FireCloudManagedGroupMembership, RegistrationInfo, RegistrationInfoV2, SamUserResponse, UserIdInfo, UserInfo, WithAccessToken, WorkbenchUserInfo}
import org.broadinstitute.dsde.rawls.model.{ErrorReportSource, RawlsUserEmail}
import org.broadinstitute.dsde.workbench.model.google.GoogleProject
import org.broadinstitute.dsde.workbench.model.{WorkbenchEmail, WorkbenchGroupName}
import org.broadinstitute.dsde.workbench.model.{WorkbenchEmail, WorkbenchGroupName, WorkbenchUserId}
import org.broadinstitute.dsde.workbench.util.health.Subsystems

import scala.concurrent.Future
Expand All @@ -35,6 +35,7 @@ trait SamDAO extends LazyLogging with ReportsSubsystemStatus {
val samGetUserIdsUrl = FireCloudConfig.Sam.baseUrl + "/api/users/v1/%s"
val samArbitraryPetTokenUrl = FireCloudConfig.Sam.baseUrl + "/api/google/v1/user/petServiceAccount/token"
val samPetKeyForProject = FireCloudConfig.Sam.baseUrl + "/api/google/v1/user/petServiceAccount/%s/key"
val samAdminGetUsersForIdsUrl = FireCloudConfig.Sam.baseUrl + "/api/admin/v2/users"

val samManagedGroupsBase: String = FireCloudConfig.Sam.baseUrl + "/api/groups"
val samManagedGroupBase: String = FireCloudConfig.Sam.baseUrl + "/api/group"
Expand All @@ -61,6 +62,8 @@ trait SamDAO extends LazyLogging with ReportsSubsystemStatus {

def getUserIds(email: RawlsUserEmail)(implicit userInfo: WithAccessToken): Future[UserIdInfo]

def getUsersForIds(samUserIds: Seq[WorkbenchUserId])(implicit userInfo: WithAccessToken): Future[Seq[WorkbenchUserInfo]]

def listWorkspaceResources(implicit userInfo: WithAccessToken): Future[Seq[UserPolicy]]

def createGroup(groupName: WorkbenchGroupName)(implicit userInfo: WithAccessToken): Future[Unit]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.broadinstitute.dsde.firecloud.model

import bio.terra.externalcreds.model.AdminLinkInfo
import org.joda.time.{DateTime, Instant}

object LinkedEraAccount {
def apply(samUserId: String, nihLink: NihLink): LinkedEraAccount = {
LinkedEraAccount(samUserId, nihLink.linkedNihUsername, Instant.ofEpochSecond(nihLink.linkExpireTime).toDateTime)
}

def apply(adminLinkInfo: AdminLinkInfo): LinkedEraAccount = {
LinkedEraAccount(adminLinkInfo.getUserId, adminLinkInfo.getLinkedExternalId, new DateTime(adminLinkInfo.getLinkExpireTime))
}

def unapply(linkedEraAccount: LinkedEraAccount): AdminLinkInfo = {
new AdminLinkInfo()
.userId(linkedEraAccount.userId)
.linkedExternalId(linkedEraAccount.linkedExternalId)
.linkExpireTime(linkedEraAccount.linkExpireTime.toDate)
}
}

case class LinkedEraAccount(userId: String, linkedExternalId: String, linkExpireTime: DateTime)


Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.broadinstitute.dsde.firecloud.model.ShareLog.{Share, ShareType}
import org.broadinstitute.dsde.rawls.model.UserModelJsonSupport._
import org.broadinstitute.dsde.rawls.model.WorkspaceACLJsonSupport.WorkspaceAccessLevelFormat
import org.broadinstitute.dsde.rawls.model._
import org.broadinstitute.dsde.workbench.client.sam.model.User
import org.broadinstitute.dsde.workbench.model.ValueObjectFormat
import org.broadinstitute.dsde.workbench.model.WorkbenchIdentityJsonSupport._
import org.broadinstitute.dsde.workbench.model.google.GoogleModelJsonSupport.InstantFormat
Expand Down Expand Up @@ -233,6 +234,7 @@ object ModelJsonProtocol extends WorkspaceJsonSupport with SprayJsonSupport {
implicit val impRegistrationInfo: RootJsonFormat[RegistrationInfo] = jsonFormat3(RegistrationInfo)
implicit val impRegistrationInfoV2: RootJsonFormat[RegistrationInfoV2] = jsonFormat3(RegistrationInfoV2)
implicit val impSamUserResponse: RootJsonFormat[SamUserResponse] = jsonFormat8(SamUserResponse)
implicit val impSamUser: RootJsonFormat[SamUser] = jsonFormat8(SamUser)
implicit val impUserIdInfo: RootJsonFormat[UserIdInfo] = jsonFormat3(UserIdInfo)
implicit val impCurator: RootJsonFormat[Curator] = jsonFormat1(Curator)
implicit val impUserImportPermission: RootJsonFormat[UserImportPermission] = jsonFormat2(UserImportPermission)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ object Profile {

}

object NihLink {
def apply(linkedEraAccount: LinkedEraAccount): NihLink = NihLink(linkedEraAccount.linkedExternalId, linkedEraAccount.linkExpireTime.getMillis / 1000)
}

case class NihLink(linkedNihUsername: String, linkExpireTime: Long) extends mappedPropVals {
require(ProfileValidator.nonEmpty(linkedNihUsername), "linkedNihUsername must be non-empty")
}
Expand Down
Loading
Loading