diff --git a/src/main/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/DirectoryDAO.scala b/src/main/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/DirectoryDAO.scala index d62cd34ac..d1581a7a0 100644 --- a/src/main/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/DirectoryDAO.scala +++ b/src/main/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/DirectoryDAO.scala @@ -75,9 +75,8 @@ trait DirectoryDAO { def acceptTermsOfService(userId: WorkbenchUserId, tosVersion: String, samRequestContext: SamRequestContext): IO[Boolean] def rejectTermsOfService(userId: WorkbenchUserId, tosVersion: String, samRequestContext: SamRequestContext): IO[Boolean] - def getLatestUserTos(userId: WorkbenchUserId, samRequestContext: SamRequestContext): IO[Option[SamUserTos]] + def getUserTos(userId: WorkbenchUserId, samRequestContext: SamRequestContext): IO[Option[SamUserTos]] def getUserTos(userId: WorkbenchUserId, tosVersion: String, samRequestContext: SamRequestContext): IO[Option[SamUserTos]] - def createPetManagedIdentity(petManagedIdentity: PetManagedIdentity, samRequestContext: SamRequestContext): IO[PetManagedIdentity] def loadPetManagedIdentity(petManagedIdentityId: PetManagedIdentityId, samRequestContext: SamRequestContext): IO[Option[PetManagedIdentity]] def getUserFromPetManagedIdentity(petManagedIdentityObjectId: ManagedIdentityObjectId, samRequestContext: SamRequestContext): IO[Option[SamUser]] diff --git a/src/main/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/PostgresDirectoryDAO.scala b/src/main/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/PostgresDirectoryDAO.scala index 5d0e57aa3..757910a8e 100644 --- a/src/main/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/PostgresDirectoryDAO.scala +++ b/src/main/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/PostgresDirectoryDAO.scala @@ -647,8 +647,9 @@ class PostgresDirectoryDAO(protected val writeDbRef: DbReference, protected val } } - override def getLatestUserTos(userId: WorkbenchUserId, samRequestContext: SamRequestContext): IO[Option[SamUserTos]] = - readOnlyTransaction("getLatestUserTos", samRequestContext) { implicit session => + // When no tosVersion is specified, return the latest TosRecord for the user + override def getUserTos(userId: WorkbenchUserId, samRequestContext: SamRequestContext): IO[Option[SamUserTos]] = + readOnlyTransaction("getUserTos", samRequestContext) { implicit session => val tosTable = TosTable.syntax val column = TosTable.column diff --git a/src/main/scala/org/broadinstitute/dsde/workbench/sam/service/TosService.scala b/src/main/scala/org/broadinstitute/dsde/workbench/sam/service/TosService.scala index 5dc199843..7163faa12 100644 --- a/src/main/scala/org/broadinstitute/dsde/workbench/sam/service/TosService.scala +++ b/src/main/scala/org/broadinstitute/dsde/workbench/sam/service/TosService.scala @@ -47,12 +47,12 @@ class TosService(val directoryDao: DirectoryDAO, val tosConfig: TermsOfServiceCo @Deprecated def getTosDetails(samUser: SamUser, samRequestContext: SamRequestContext): IO[TermsOfServiceDetails] = - directoryDao.getLatestUserTos(samUser.id, samRequestContext).map { tos => + directoryDao.getUserTos(samUser.id, samRequestContext).map { tos => TermsOfServiceDetails(isEnabled = true, tosConfig.isGracePeriodEnabled, tosConfig.version, tos.map(_.version)) } def getTosComplianceStatus(samUser: SamUser, samRequestContext: SamRequestContext): IO[TermsOfServiceComplianceStatus] = for { - latestUserTos <- directoryDao.getLatestUserTos(samUser.id, samRequestContext) + latestUserTos <- directoryDao.getUserTos(samUser.id, samRequestContext) previousUserTos <- directoryDao.getUserTos(samUser.id, tosConfig.rollingAcceptanceWindowPreviousTosVersion, samRequestContext) userHasAcceptedLatestVersion = userHasAcceptedLatestTosVersion(latestUserTos) permitsSystemUsage = tosAcceptancePermitsSystemUsage(samUser, latestUserTos, previousUserTos) @@ -63,7 +63,14 @@ class TosService(val directoryDao: DirectoryDAO, val tosConfig: TermsOfServiceCo private def tosAcceptancePermitsSystemUsage(user: SamUser, userTos: Option[SamUserTos], previousUserTos: Option[SamUserTos]): Boolean = { val now = Instant.now() val userIsServiceAccount = StandardSamUserDirectives.SAdomain.matches(user.email.value) // Service Account users do not need to accept ToS - val userIsPermitted = userTos.exists { tos => + + if (userIsServiceAccount) { + return true + } + if (userHasRejectedLatestTosVersion(userTos)) { + return false + } + userTos.exists { tos => val userHasAcceptedLatestVersion = userHasAcceptedLatestTosVersion(Option(tos)) val userCanUseSystemUnderGracePeriod = tosConfig.isGracePeriodEnabled && tos.action == TosTable.ACCEPT val tosDisabled = !tosConfig.isTosEnabled @@ -74,7 +81,6 @@ class TosService(val directoryDao: DirectoryDAO, val tosConfig: TermsOfServiceCo userHasAcceptedLatestVersion || userInsideOfRollingAcceptanceWindow || userCanUseSystemUnderGracePeriod || tosDisabled } - userIsPermitted || userIsServiceAccount } private def userHasAcceptedLatestTosVersion(userTos: Option[SamUserTos]): Boolean = @@ -82,6 +88,11 @@ class TosService(val directoryDao: DirectoryDAO, val tosConfig: TermsOfServiceCo tos.version.contains(tosConfig.version) && tos.action == TosTable.ACCEPT } + private def userHasRejectedLatestTosVersion(userTos: Option[SamUserTos]): Boolean = + userTos.exists { tos => + tos.version.contains(tosConfig.version) && tos.action == TosTable.REJECT + } + private def userHasAcceptedPreviousTosVersion(previousUserTos: Option[SamUserTos]): Boolean = previousUserTos.exists(tos => tos.action == TosTable.ACCEPT) } 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..8f4bda1c1 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, "1", "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, "1", "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..a50b7020e 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 @@ -340,7 +340,14 @@ class MockDirectoryDAO(val groups: mutable.Map[WorkbenchGroupIdentity, Workbench true } - override def getLatestUserTos(userId: WorkbenchUserId, samRequestContext: SamRequestContext): IO[Option[SamUserTos]] = + override def getUserTos(userId: WorkbenchUserId, samRequestContext: SamRequestContext): IO[Option[SamUserTos]] = + loadUser(userId, samRequestContext).map { + case None => None + case Some(_) => + userTos.get(userId) + } + + override def getUserTos(userId: WorkbenchUserId, tosVersion: String, samRequestContext: SamRequestContext): IO[Option[SamUserTos]] = loadUser(userId, samRequestContext).map { case None => None case Some(_) => diff --git a/src/test/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/PostgresDirectoryDAOSpec.scala b/src/test/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/PostgresDirectoryDAOSpec.scala index da09d7617..123b12003 100644 --- a/src/test/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/PostgresDirectoryDAOSpec.scala +++ b/src/test/scala/org/broadinstitute/dsde/workbench/sam/dataAccess/PostgresDirectoryDAOSpec.scala @@ -1486,7 +1486,7 @@ class PostgresDirectoryDAOSpec extends RetryableAnyFreeSpec with Matchers with B dao.acceptTermsOfService(defaultUser.id, tosConfig.version, samRequestContext).unsafeRunSync() shouldBe true // Assert - val userTos = dao.getLatestUserTos(defaultUser.id, samRequestContext).unsafeRunSync() + val userTos = dao.getUserTos(defaultUser.id, samRequestContext).unsafeRunSync() userTos should not be empty userTos.get.createdAt should beAround(Instant.now()) userTos.get.action shouldBe TosTable.ACCEPT @@ -1501,7 +1501,7 @@ class PostgresDirectoryDAOSpec extends RetryableAnyFreeSpec with Matchers with B dao.acceptTermsOfService(defaultUser.id, "2", samRequestContext).unsafeRunSync() shouldBe true // Assert - val userTos = dao.getLatestUserTos(defaultUser.id, samRequestContext).unsafeRunSync() + val userTos = dao.getUserTos(defaultUser.id, samRequestContext).unsafeRunSync() userTos should not be empty userTos.get.createdAt should beAround(Instant.now()) userTos.get.action shouldBe TosTable.ACCEPT @@ -1516,7 +1516,7 @@ class PostgresDirectoryDAOSpec extends RetryableAnyFreeSpec with Matchers with B dao.rejectTermsOfService(user.id, tosConfig.version, samRequestContext).unsafeRunSync() shouldBe true // Assert - val userTos = dao.getLatestUserTos(user.id, samRequestContext).unsafeRunSync() + val userTos = dao.getUserTos(user.id, samRequestContext).unsafeRunSync() userTos should not be empty userTos.get.createdAt should beAround(Instant.now()) userTos.get.action shouldBe TosTable.REJECT @@ -1530,7 +1530,7 @@ class PostgresDirectoryDAOSpec extends RetryableAnyFreeSpec with Matchers with B dao.rejectTermsOfService(user.id, tosConfig.version, samRequestContext).unsafeRunSync() shouldBe true // Assert - val userTos = dao.getLatestUserTos(user.id, samRequestContext).unsafeRunSync() + val userTos = dao.getUserTos(user.id, samRequestContext).unsafeRunSync() userTos should not be empty userTos.get.createdAt should beAround(Instant.now()) userTos.get.action shouldBe TosTable.REJECT @@ -1544,7 +1544,7 @@ class PostgresDirectoryDAOSpec extends RetryableAnyFreeSpec with Matchers with B dao.createUser(user, samRequestContext).unsafeRunSync() // Assert - val userTos = dao.getLatestUserTos(user.id, samRequestContext).unsafeRunSync() + val userTos = dao.getUserTos(user.id, samRequestContext).unsafeRunSync() userTos should be(None) } } 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..e286fd268 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 @@ -85,10 +85,15 @@ class TosServiceSpec(_system: ActorSystem) "always allows service account users to use the system" in { val tosVersion = "2" + val previousTosVersion = "1" val tosService = - new TosService(dirDAO, TestSupport.tosConfig.copy(version = tosVersion)) - when(dirDAO.getLatestUserTos(serviceAccountUser.id, samRequestContext)) + new TosService(dirDAO, TestSupport.tosConfig.copy(version = tosVersion, rollingAcceptanceWindowPreviousTosVersion=previousTosVersion)) + when(dirDAO.getUserTos(serviceAccountUser.id, samRequestContext)) .thenReturn(IO.pure(Some(SamUserTos(serviceAccountUser.id, tosVersion, TosTable.ACCEPT, Instant.now())))) + + when(dirDAO.getUserTos(serviceAccountUser.id, previousTosVersion, 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,134 +105,261 @@ class TosServiceSpec(_system: ActorSystem) } val tosVersion = "2" + val rollingAcceptanceWindowPreviousTosVersion = "1" + val rollingAcceptanceWindowExpiration = Instant.now().plusSeconds(3600) 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)) - - /** | Case | Grace Period Enabled | Accepted Version | Current Version | User accepted latest | Permits system usage | - * |:-----|:---------------------|:-----------------|:----------------|:---------------------|:---------------------| - * | 1 | false | null | "2" | false | false | - * | 2 | false | "0" | "2" | false | false | - * | 3 | false | "2" | "2" | true | true | - * | 4 | true | null | "2" | false | false | - * | 5 | true | "0" | "2" | false | true | - * | 6 | true | "2" | "2" | true | true | + val tosServiceV2GracePeriodDisabledAcceptanceWindowDisabled = new TosService(dirDAO, TestSupport.tosConfig.copy(isTosEnabled=true, isGracePeriodEnabled=false, version = tosVersion, rollingAcceptanceWindowPreviousTosVersion=rollingAcceptanceWindowPreviousTosVersion)) + val tosServiceV2GracePeriodEnabledAcceptanceWindowDisabled = + new TosService(dirDAO, TestSupport.tosConfig.copy(version = tosVersion, isGracePeriodEnabled = true, rollingAcceptanceWindowPreviousTosVersion=rollingAcceptanceWindowPreviousTosVersion)) + val tosServiceV2GracePeriodDisabledAcceptanceWindowEnabled = new TosService(dirDAO, TestSupport.tosConfig.copy(isTosEnabled=true, isGracePeriodEnabled=false, version = tosVersion, rollingAcceptanceWindowExpiration = rollingAcceptanceWindowExpiration, rollingAcceptanceWindowPreviousTosVersion = rollingAcceptanceWindowPreviousTosVersion)) + val tosServiceV2GracePeriodEnabledAcceptanceWindowEnabled = new TosService(dirDAO, TestSupport.tosConfig.copy(isTosEnabled=true, isGracePeriodEnabled=true, version = tosVersion, rollingAcceptanceWindowExpiration = rollingAcceptanceWindowExpiration, rollingAcceptanceWindowPreviousTosVersion = rollingAcceptanceWindowPreviousTosVersion)) + + // Note there is an assumption that the previous version of the ToS is always 1 version behind the current version + /** | Case | Grace Period Enabled | Inside Acceptance Window | Accepted Version | Current Version | User accepted latest | Permits system usage | + * |:-----|:---------------------|:-------------------------|:-----------------|:----------------|:---------------------|:---------------------| + * | 1 | false | false | null | "2" | false | false | + * | 2 | false | false | "1" | "2" | false | false | + * | 3 | false | false | "2" | "2" | true | true | + * | 4 | true | flase | null | "2" | false | false | + * | 5 | true | flase | "1" | "2" | false | true | + * | 6 | true | flase | "2" | "2" | true | true | + * | 7 | false | true | null | "2" | false | false | + * | 8 | false | true | "1" | "2" | false | true | + * | 9 | false | true | "2" | "2" | true | true | + * | 10 | true | true | null | "2" | false | false | + * | 11 | true | true | "1" | "2" | false | true | + * | 12 | true | true | "2" | "2" | true | true | */ "when the user has not accepted any ToS version" - { - "says the user has not accepted the latest version" in { - when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) - .thenReturn(IO.pure(None)) - val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() - complianceStatus.userHasAcceptedLatestTos shouldBe false + withoutGracePeriod - { + withoutRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(None)) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(None)) + // CASE 1 + val complianceStatus = tosServiceV2GracePeriodDisabledAcceptanceWindowDisabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe false + } + } + } + withGracePeriod - { + withoutRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(None)) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(None)) + // CASE 4 + val complianceStatus = tosServiceV2GracePeriodEnabledAcceptanceWindowDisabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe false + } + } } withoutGracePeriod - { - cannotUseTheSystem in { - when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) - .thenReturn(IO.pure(None)) - // CASE 1 - val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() - complianceStatus.permitsSystemUsage shouldBe false + withRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(None)) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(None)) + // CASE 7 + val complianceStatus = tosServiceV2GracePeriodDisabledAcceptanceWindowEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe false + } } } withGracePeriod - { - cannotUseTheSystem in { - when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) - .thenReturn(IO.pure(None)) - // CASE 4 - val complianceStatus = tosServiceV2GracePeriodEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() - complianceStatus.permitsSystemUsage shouldBe false + withRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getUserTos(defaultUser.id, samRequestContext)) + .thenReturn(IO.pure(None)) + when(dirDAO.getUserTos(defaultUser.id, rollingAcceptanceWindowPreviousTosVersion, samRequestContext)) + .thenReturn(IO.pure(None)) + // CASE 10 + val complianceStatus = tosServiceV2GracePeriodEnabledAcceptanceWindowEnabled.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)) - val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() - complianceStatus.userHasAcceptedLatestTos shouldBe false + "when the user has accepted the previous ToS version" - { + withoutGracePeriod - { + withoutRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getUserTos(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 2 + val complianceStatus = tosServiceV2GracePeriodDisabledAcceptanceWindowDisabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe false + } + } + } + withGracePeriod - { + withoutRollingAcceptanceWindow - { + canUseTheSystem in { + when(dirDAO.getUserTos(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 5 + val complianceStatus = tosServiceV2GracePeriodEnabledAcceptanceWindowDisabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe true + } + } } withoutGracePeriod - { - cannotUseTheSystem in { - when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) - .thenReturn(IO.pure(None)) - // CASE 2 - val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() - complianceStatus.permitsSystemUsage shouldBe false + withRollingAcceptanceWindow - { + canUseTheSystem in { + when(dirDAO.getUserTos(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 8 + val complianceStatus = tosServiceV2GracePeriodDisabledAcceptanceWindowEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe true + } } } withGracePeriod - { - canUseTheSystem in { - when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) - .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.ACCEPT, Instant.now())))) - // CASE 5 - val complianceStatus = tosServiceV2GracePeriodEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() - complianceStatus.permitsSystemUsage shouldBe true + withRollingAcceptanceWindow - { + canUseTheSystem in { + when(dirDAO.getUserTos(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 11 + val complianceStatus = tosServiceV2GracePeriodEnabledAcceptanceWindowEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe true + } } } } "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())))) - val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() - complianceStatus.userHasAcceptedLatestTos shouldBe true + withoutGracePeriod - { + withoutRollingAcceptanceWindow - { + canUseTheSystem in { + when(dirDAO.getUserTos(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 = tosServiceV2GracePeriodDisabledAcceptanceWindowDisabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe true + } + } + } + withGracePeriod - { + withoutRollingAcceptanceWindow - { + canUseTheSystem in { + when(dirDAO.getUserTos(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 = tosServiceV2GracePeriodEnabledAcceptanceWindowDisabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe true + } + } } withoutGracePeriod - { - canUseTheSystem in { - when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) - .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.ACCEPT, Instant.now())))) - // CASE 3 - val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() - complianceStatus.permitsSystemUsage shouldBe true + withRollingAcceptanceWindow - { + canUseTheSystem in { + when(dirDAO.getUserTos(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 9 + val complianceStatus = tosServiceV2GracePeriodDisabledAcceptanceWindowEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe true + } } } withGracePeriod - { - canUseTheSystem in { - when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) - .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.ACCEPT, Instant.now())))) - // CASE 6 - val complianceStatus = tosServiceV2GracePeriodEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() - complianceStatus.permitsSystemUsage shouldBe true + withRollingAcceptanceWindow - { + canUseTheSystem in { + when(dirDAO.getUserTos(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 12 + val complianceStatus = tosServiceV2GracePeriodEnabledAcceptanceWindowEnabled.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())))) - val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() - complianceStatus.userHasAcceptedLatestTos shouldBe false + withoutGracePeriod - { + withoutRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getUserTos(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 = tosServiceV2GracePeriodDisabledAcceptanceWindowDisabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe false + } + } + } + withGracePeriod - { + withoutRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getUserTos(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 = tosServiceV2GracePeriodEnabledAcceptanceWindowDisabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe false + } + } } withoutGracePeriod - { - cannotUseTheSystem in { - when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) - .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.REJECT, Instant.now())))) - // CASE 1 - val complianceStatus = tosServiceV2.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() - complianceStatus.permitsSystemUsage shouldBe false + withRollingAcceptanceWindow - { + canUseTheSystem in { + when(dirDAO.getUserTos(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 = tosServiceV2GracePeriodDisabledAcceptanceWindowEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() + complianceStatus.permitsSystemUsage shouldBe false + } } } withGracePeriod - { - cannotUseTheSystem in { - when(dirDAO.getLatestUserTos(defaultUser.id, samRequestContext)) - .thenReturn(IO.pure(Option(SamUserTos(defaultUser.id, tosVersion, TosTable.REJECT, Instant.now())))) - // CASE 4 - val complianceStatus = tosServiceV2GracePeriodEnabled.getTosComplianceStatus(defaultUser, samRequestContext).unsafeRunSync() - complianceStatus.permitsSystemUsage shouldBe false + withRollingAcceptanceWindow - { + cannotUseTheSystem in { + when(dirDAO.getUserTos(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 = tosServiceV2GracePeriodEnabledAcceptanceWindowEnabled.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)) + when(dirDAO.getUserTos(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() + val complianceStatus = tosServiceV2GracePeriodDisabledAcceptanceWindowDisabled.getTosComplianceStatus(serviceAccountUser, samRequestContext).unsafeRunSync() complianceStatus.permitsSystemUsage shouldBe true } }