diff --git a/src/test/resources/reference.conf b/src/test/resources/reference.conf index 4d95e02b2..d0963f957 100644 --- a/src/test/resources/reference.conf +++ b/src/test/resources/reference.conf @@ -62,6 +62,8 @@ termsOfService { enabled = false version = 1 url = "app.terra.bio/#terms-of-service" + rollingAcceptanceWindowExpirationDatetime = "2019-01-01T00:00:00Z" + rollingAcceptanceWindowPreviousTosVersion = 0 } petServiceAccount { 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 9ad9e0af7..b5badfaf1 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 @@ -24,6 +24,7 @@ import org.broadinstitute.dsde.workbench.sam.util.SamRequestContext import org.broadinstitute.dsde.workbench.sam.{Generator, TestSupport} import org.scalatest.concurrent.ScalaFutures +import java.time.Instant import scala.concurrent.ExecutionContext /** Created by dvoet on 7/14/17. @@ -49,7 +50,7 @@ class TestSamRoutes( userService, statusService, managedGroupService, - TermsOfServiceConfig(true, false, "0", "app.terra.bio/#terms-of-service"), + TermsOfServiceConfig(true, false, "0", "app.terra.bio/#terms-of-service", Instant.now(), "0"), policyEvaluatorService, tosService, LiquibaseConfig("", false), @@ -91,7 +92,7 @@ class TestSamTosEnabledRoutes( userService, statusService, managedGroupService, - TermsOfServiceConfig(true, false, "0", "app.terra.bio/#terms-of-service"), + TermsOfServiceConfig(true, false, "0", "app.terra.bio/#terms-of-service", Instant.now(), "0"), policyEvaluatorService, tosService, LiquibaseConfig("", false), diff --git a/src/test/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/MockDirectoryDAO.scala b/src/test/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/MockDirectoryDAO.scala index 3472df795..64f0bb4ef 100644 --- a/src/test/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/MockDirectoryDAO.scala +++ b/src/test/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/MockDirectoryDAO.scala @@ -347,6 +347,13 @@ class MockDirectoryDAO(val groups: mutable.Map[WorkbenchGroupIdentity, Workbench userTos.get(userId) } + override def getUserTos(userId: WorkbenchUserId, tosVersion: String, samRequestContext: SamRequestContext): IO[Option[SamUserTos]] = + loadUser(userId, samRequestContext).map { + case None => None + case Some(_) => + userTos.get(userId) + } + override def createPetManagedIdentity(petManagedIdentity: PetManagedIdentity, samRequestContext: SamRequestContext): IO[PetManagedIdentity] = { if (petManagedIdentitiesByUser.keySet.contains(petManagedIdentity.id)) { IO.raiseError(new WorkbenchExceptionWithErrorReport(ErrorReport(StatusCodes.Conflict, s"pet managed identity ${petManagedIdentity.id} already exists"))) diff --git a/src/test/scala/org/broadinstitute/dsde/workbench/sam/service/TosServiceSpec.scala b/src/test/scala/org/broadinstitute/dsde/workbench/sam/service/TosServiceSpec.scala index 7f6b56828..77a8eda03 100644 --- a/src/test/scala/org/broadinstitute/dsde/workbench/sam/service/TosServiceSpec.scala +++ b/src/test/scala/org/broadinstitute/dsde/workbench/sam/service/TosServiceSpec.scala @@ -89,6 +89,10 @@ class TosServiceSpec(_system: ActorSystem) new TosService(dirDAO, TestSupport.tosConfig.copy(version = tosVersion)) when(dirDAO.getLatestUserTos(serviceAccountUser.id, samRequestContext)) .thenReturn(IO.pure(Some(SamUserTos(serviceAccountUser.id, tosVersion, TosTable.ACCEPT, Instant.now())))) + + when(dirDAO.getUserTos(serviceAccountUser.id, "0", samRequestContext)) + .thenReturn(IO.pure(Some(SamUserTos(serviceAccountUser.id, tosVersion, TosTable.ACCEPT, Instant.now())))) + val complianceStatus = tosService.getTosComplianceStatus(serviceAccountUser, samRequestContext).unsafeRunSync() complianceStatus.permitsSystemUsage shouldBe true } @@ -100,13 +104,18 @@ class TosServiceSpec(_system: ActorSystem) } val tosVersion = "2" + val rollingAcceptanceWindowPreviousTosVersion = "0" val withoutGracePeriod = "without the grace period enabled" val withGracePeriod = " with the grace period enabled" + val withoutRollingAcceptanceWindow = "outside of the rolling acceptance window" + val withRollingAcceptanceWindow = " inside of the rolling acceptance window" val cannotUseTheSystem = "says the user cannot use the system" val canUseTheSystem = "says the user can use the system" val tosServiceV2 = new TosService(dirDAO, TestSupport.tosConfig.copy(version = tosVersion)) val tosServiceV2GracePeriodEnabled = new TosService(dirDAO, TestSupport.tosConfig.copy(version = tosVersion, isGracePeriodEnabled = true)) + val tosServiceV2AcceptanceWindowEnabled = new TosService(dirDAO, TestSupport.tosConfig.copy(isTosEnabled=true, isGracePeriodEnabled=false, version = tosVersion, rollingAcceptanceWindowExpiration = Instant.now().plusSeconds(3600), rollingAcceptanceWindowPreviousTosVersion = rollingAcceptanceWindowPreviousTosVersion)) + /** | Case | Grace Period Enabled | Accepted Version | Current Version | User accepted latest | Permits system usage | * |:-----|:---------------------|:-----------------|:----------------|:---------------------|:---------------------| @@ -122,6 +131,9 @@ class TosServiceSpec(_system: ActorSystem) "says the user has not accepted the latest version" in { when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) .thenReturn(IO.pure(None)) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(None)) + val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() complianceStatus.userHasAcceptedLatestTos shouldBe false } @@ -129,6 +141,8 @@ class TosServiceSpec(_system: ActorSystem) cannotUseTheSystem in { when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) .thenReturn(IO.pure(None)) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(None)) // CASE 1 val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() complianceStatus.permitsSystemUsage shouldBe false @@ -138,16 +152,42 @@ class TosServiceSpec(_system: ActorSystem) cannotUseTheSystem in { when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) .thenReturn(IO.pure(None)) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(None)) // CASE 4 val complianceStatus = tosServiceV2GracePeriodEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() complianceStatus.permitsSystemUsage shouldBe false } } + withRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(None)) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(None)) + // CASE 1 + val complianceStatus = tosServiceV2AcceptanceWindowEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe false + } + } + withoutRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(None)) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(None)) + // CASE 4 + val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe false + } + } } "when the user has accepted a non-current ToS version" - { "says the user has not accepted the latest version" in { when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) .thenReturn(IO.pure(None)) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() complianceStatus.userHasAcceptedLatestTos shouldBe false } @@ -155,6 +195,8 @@ class TosServiceSpec(_system: ActorSystem) cannotUseTheSystem in { when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) .thenReturn(IO.pure(None)) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) // CASE 2 val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() complianceStatus.permitsSystemUsage shouldBe false @@ -164,16 +206,53 @@ class TosServiceSpec(_system: ActorSystem) canUseTheSystem in { when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.ACCEPT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) // CASE 5 val complianceStatus = tosServiceV2GracePeriodEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() complianceStatus.permitsSystemUsage shouldBe true } } + withRollingAcceptanceWindow - { + canUseTheSystem in { + when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) + // CASE 1 + val complianceStatus = tosServiceV2AcceptanceWindowEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe true + } + } + withRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) + // CASE 1 + val complianceStatus = tosServiceV2AcceptanceWindowEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe true + } + } + withoutRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) + // CASE 4 + val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe false + } + } } "when the user has accepted the current ToS version" - { "says the user has accepted the latest version" in { when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.ACCEPT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() complianceStatus.userHasAcceptedLatestTos shouldBe true } @@ -181,6 +260,8 @@ class TosServiceSpec(_system: ActorSystem) canUseTheSystem in { when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.ACCEPT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) // CASE 3 val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() complianceStatus.permitsSystemUsage shouldBe true @@ -190,17 +271,43 @@ class TosServiceSpec(_system: ActorSystem) canUseTheSystem in { when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.ACCEPT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) // CASE 6 val complianceStatus = tosServiceV2GracePeriodEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() complianceStatus.permitsSystemUsage shouldBe true } } + withRollingAcceptanceWindow - { + canUseTheSystem in { + when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.ACCEPT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) + // CASE 1 + val complianceStatus = tosServiceV2AcceptanceWindowEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe true + } + } + withoutRollingAcceptanceWindow - { + canUseTheSystem in { + when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.ACCEPT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) + // CASE 4 + val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe true + } + } } "when the user has rejected the latest ToS version" - { "says the user has rejected the latest version" in { when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.REJECT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() complianceStatus.userHasAcceptedLatestTos shouldBe false } @@ -208,6 +315,8 @@ class TosServiceSpec(_system: ActorSystem) cannotUseTheSystem in { when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.REJECT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) // CASE 1 val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() complianceStatus.permitsSystemUsage shouldBe false @@ -217,16 +326,42 @@ class TosServiceSpec(_system: ActorSystem) cannotUseTheSystem in { when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.REJECT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) // CASE 4 val complianceStatus = tosServiceV2GracePeriodEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() complianceStatus.permitsSystemUsage shouldBe false } } + withRollingAcceptanceWindow - { + canUseTheSystem in { + when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.REJECT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) + // CASE 1 + val complianceStatus = tosServiceV2AcceptanceWindowEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe true + } + } + withoutRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.REJECT, Instant.now())))) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, TosTable.ACCEPT, Instant.now())))) + // CASE 4 + val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe false + } + } } "when a service account is using the api" - { "let it use the api regardless of tos status" in { when(dirDAO.getLatestUserTos(serviceAccountUser.id, samRequestContext)) .thenReturn(IO.pure(None)) + when(dirDAO.getUserTos(serviceAccountUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(None)) val complianceStatus = tosServiceV2.getTosComplianceStatus(serviceAccountUser, samRequestContext).unsafeRunSync() complianceStatus.permitsSystemUsage shouldBe true }