diff --git a/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/HttpSamDAO.scala b/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/HttpSamDAO.scala index c5f95ed0ab..dbfa0557da 100644 --- a/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/HttpSamDAO.scala +++ b/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/HttpSamDAO.scala @@ -519,9 +519,10 @@ class HttpSamDAO(baseSamServiceURL: String, rawlsCredential: RawlsCredential, ti } } - override def listResourcesWithActions(resourceTypeName: SamResourceTypeName, - action: SamResourceAction, - ctx: RawlsRequestContext + override def listResourcesWithRolesOrActions(resourceTypeName: SamResourceTypeName, + actions: Seq[SamResourceAction], + roles: Seq[SamResourceRole], + ctx: RawlsRequestContext ): Future[Seq[SamUserResource]] = retry(when401or5xx) { () => val callback = new SamApiCallback[ListResourcesV2200Response]("listResourcesV2") @@ -530,8 +531,8 @@ class HttpSamDAO(baseSamServiceURL: String, rawlsCredential: RawlsCredential, ti /* format = */ "hierarchical", /* resourceTypes = */ util.List.of(resourceTypeName.value), /* policies = */ util.List.of(), - /* roles = */ util.List.of, - /* actions = */ util.List.of(action.value), + /* roles = */ roles.map(_.toString).asJava, + /* actions = */ actions.map(_.toString).asJava, /* includePublic = */ false, callback ) diff --git a/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/SamDAO.scala b/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/SamDAO.scala index dd19bb20c7..185e190ccc 100644 --- a/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/SamDAO.scala +++ b/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/SamDAO.scala @@ -100,9 +100,10 @@ trait SamDAO { def listUserResources(resourceTypeName: SamResourceTypeName, ctx: RawlsRequestContext): Future[Seq[SamUserResource]] - def listResourcesWithActions(resourceTypeName: SamResourceTypeName, - action: SamResourceAction, - ctx: RawlsRequestContext + def listResourcesWithRolesOrActions(resourceTypeName: SamResourceTypeName, + actions: Seq[SamResourceAction], + roles: Seq[SamResourceRole], + ctx: RawlsRequestContext ): Future[Seq[SamUserResource]] def listPoliciesForResource(resourceTypeName: SamResourceTypeName, diff --git a/core/src/main/scala/org/broadinstitute/dsde/rawls/spendreporting/SpendReportingService.scala b/core/src/main/scala/org/broadinstitute/dsde/rawls/spendreporting/SpendReportingService.scala index dc001a81ab..9353803f19 100644 --- a/core/src/main/scala/org/broadinstitute/dsde/rawls/spendreporting/SpendReportingService.scala +++ b/core/src/main/scala/org/broadinstitute/dsde/rawls/spendreporting/SpendReportingService.scala @@ -568,15 +568,26 @@ class SpendReportingService( def getBillingWithSpendPermission( ): Future[Map[BillingProjectSpendExport, Seq[(GoogleProjectId, WorkspaceName)]]] = for { - billingProjectResources <- samDAO.listResourcesWithActions(SamResourceTypeNames.billingProject, - SamBillingProjectActions.readSpendReport, - ctx + billingProjectResources <- samDAO.listResourcesWithRolesOrActions(SamResourceTypeNames.billingProject, + List(SamBillingProjectActions.readSpendReport), + List(), + ctx + ) + ownerWorkspaces <- samDAO.listResourcesWithRolesOrActions( + SamResourceTypeNames.workspace, + List(), + List(SamWorkspaceRoles.owner), // TODO: owner only or owner + project-owner? + ctx ) billingProjectIds = billingProjectResources.map(resource => RawlsBillingProjectName(resource.resourceId)).toList groupedWorkspaces <- workspaceServiceConstructor(ctx).getGCPWorkspacesByBillingProjects(billingProjectIds) + ownerWorkspaceSet = ownerWorkspaces.map(_.resourceId).toSet + filteredGroupedWorkspaces = groupedWorkspaces.map { case (key, workspaces) => + key -> workspaces.filter(ws => ownerWorkspaceSet.contains(ws.name)) + } // Only use the BPs we know exist in the DB and are GCP - spendConfigs <- getSpendExportConfigurations(groupedWorkspaces.keys.toList) + spendConfigs <- getSpendExportConfigurations(filteredGroupedWorkspaces.keys.toList) } yield spendConfigs.map { config => - config -> groupedWorkspaces(config.billingProjectName).map(ws => (ws.googleProjectId, ws.toWorkspaceName)) + config -> filteredGroupedWorkspaces(config.billingProjectName).map(ws => (ws.googleProjectId, ws.toWorkspaceName)) }.toMap } diff --git a/core/src/test/scala/org/broadinstitute/dsde/rawls/mock/MockSamDAO.scala b/core/src/test/scala/org/broadinstitute/dsde/rawls/mock/MockSamDAO.scala index 31e6110cbb..7ee3dbf1c6 100644 --- a/core/src/test/scala/org/broadinstitute/dsde/rawls/mock/MockSamDAO.scala +++ b/core/src/test/scala/org/broadinstitute/dsde/rawls/mock/MockSamDAO.scala @@ -239,9 +239,10 @@ class MockSamDAO(dataSource: SlickDataSource)(implicit executionContext: Executi case _ => Future.successful(Seq.empty) } - override def listResourcesWithActions(resourceTypeName: SamResourceTypeName, - action: SamResourceAction, - ctx: RawlsRequestContext + override def listResourcesWithRolesOrActions(resourceTypeName: SamResourceTypeName, + actions: Seq[SamResourceAction], + roles: Seq[SamResourceRole], + ctx: RawlsRequestContext ): Future[Seq[SamUserResource]] = resourceTypeName match { case SamResourceTypeNames.workspace => @@ -251,7 +252,7 @@ class MockSamDAO(dataSource: SlickDataSource)(implicit executionContext: Executi _.map(workspace => SamUserResource( workspace.workspaceId, - SamRolesAndActions(Set(SamWorkspaceRoles.owner), Set(action)), + SamRolesAndActions(roles.toSet, actions.toSet), SamRolesAndActions(Set.empty, Set.empty), SamRolesAndActions(Set.empty, Set.empty), Set.empty, @@ -267,7 +268,7 @@ class MockSamDAO(dataSource: SlickDataSource)(implicit executionContext: Executi _.map(project => SamUserResource( project.projectName.value, - SamRolesAndActions(Set(SamBillingProjectRoles.owner), Set(action)), + SamRolesAndActions(roles.toSet, actions.toSet), SamRolesAndActions(Set.empty, Set.empty), SamRolesAndActions(Set.empty, Set.empty), Set.empty, @@ -426,16 +427,17 @@ class CustomizableMockSamDAO(dataSource: SlickDataSource)(implicit executionCont } } - override def listResourcesWithActions(resourceTypeName: SamResourceTypeName, - action: SamResourceAction, - ctx: RawlsRequestContext + override def listResourcesWithRolesOrActions(resourceTypeName: SamResourceTypeName, + actions: Seq[SamResourceAction], + roles: Seq[SamResourceRole], + ctx: RawlsRequestContext ): Future[Seq[SamUserResource]] = { val userResources = for { ((typeName, resourceId), resourcePolicies) <- policies if typeName == resourceTypeName userResource <- constructResourceFromPolicies(ctx, resourceId, resourcePolicies.values) } yield userResource if (userResources.isEmpty) { - super.listResourcesWithActions(resourceTypeName, action, ctx) + super.listResourcesWithRolesOrActions(resourceTypeName, actions, roles, ctx) } else { Future.successful(userResources.toSeq) } diff --git a/core/src/test/scala/org/broadinstitute/dsde/rawls/spendreporting/SpendReportingServiceSpec.scala b/core/src/test/scala/org/broadinstitute/dsde/rawls/spendreporting/SpendReportingServiceSpec.scala index 72b8acd7db..a5f1477c7f 100644 --- a/core/src/test/scala/org/broadinstitute/dsde/rawls/spendreporting/SpendReportingServiceSpec.scala +++ b/core/src/test/scala/org/broadinstitute/dsde/rawls/spendreporting/SpendReportingServiceSpec.scala @@ -1344,7 +1344,48 @@ class SpendReportingServiceSpec extends AnyFlatSpecLike with Matchers with Mocki ) ) .when(samDAO) - .listResourcesWithActions(any(), any(), any()) + .listResourcesWithRolesOrActions(mockitoEq(SamResourceTypeNames.billingProject), any(), any(), any()) + + doReturn( + Future.successful( + Seq( + SamUserResource( + "workspace1Billing1", + SamRolesAndActions(Set.empty, Set.empty), + SamRolesAndActions(Set.empty, Set.empty), + SamRolesAndActions(Set.empty, Set.empty), + Set.empty, + Set.empty + ), + SamUserResource( + "workspace2Billing1", + SamRolesAndActions(Set.empty, Set.empty), + SamRolesAndActions(Set.empty, Set.empty), + SamRolesAndActions(Set.empty, Set.empty), + Set.empty, + Set.empty + ), + SamUserResource( + "workspace1Billing2", + SamRolesAndActions(Set.empty, Set.empty), + SamRolesAndActions(Set.empty, Set.empty), + SamRolesAndActions(Set.empty, Set.empty), + Set.empty, + Set.empty + ), + SamUserResource( + "workspace1Billing3", + SamRolesAndActions(Set.empty, Set.empty), + SamRolesAndActions(Set.empty, Set.empty), + SamRolesAndActions(Set.empty, Set.empty), + Set.empty, + Set.empty + ) + ) + ) + ) + .when(samDAO) + .listResourcesWithRolesOrActions(mockitoEq(SamResourceTypeNames.workspace), any(), any(), any()) val workspace1Billing1 = TestData.workspace("workspace1Billing1",