From 89fbc649d5a6b81718e43768aa2f7aa45b2374bb Mon Sep 17 00:00:00 2001 From: Alex Henning Johannessen Date: Tue, 4 Apr 2023 10:51:57 +0100 Subject: [PATCH 01/30] all: prepare for Scala3 - Avoiding automatic dependency on akka http server makes it easier for dependant projects to use Scala 3 and netty instead. - adjust tests to use mockito instead of specs2 mock wrappers. - drop jdk8 from CI --- .gitignore | 10 +- build.sbt | 28 +- project/Dependencies.scala | 2 +- project/plugins.sbt | 2 +- .../impl/providers/CasProviderSpec.scala | 16 +- .../CacheAuthenticatorRepositorySpec.scala | 32 +- .../DelegableAuthInfoRepositorySpec.scala | 34 +- .../providers/GoogleTotpProviderSpec.scala | 15 +- .../authenticators/CookieAuthenticator.scala | 2 +- .../authenticators/SessionAuthenticator.scala | 2 +- silhouette/test/Helpers.scala | 17 +- .../api/actions/SecuredActionSpec.scala | 302 +++++++++--------- .../api/actions/UnsecuredActionSpec.scala | 72 +++-- .../api/actions/UserAwareActionSpec.scala | 190 +++++------ .../BearerTokenAuthenticatorSpec.scala | 96 +++--- .../CookieAuthenticatorSpec.scala | 182 +++++------ .../DummyAuthenticatorSpec.scala | 3 +- .../authenticators/JWTAuthenticatorSpec.scala | 142 ++++---- .../SessionAuthenticatorSpec.scala | 84 ++--- .../providers/BasicAuthProviderSpec.scala | 39 +-- .../providers/CredentialsProviderSpec.scala | 35 +- .../DefaultSocialStateHandlerSpec.scala | 64 ++-- .../impl/providers/OAuth1ProviderSpec.scala | 66 ++-- .../impl/providers/OAuth2ProviderSpec.scala | 96 +++--- .../impl/providers/OpenIDProviderSpec.scala | 27 +- .../impl/providers/PasswordProviderSpec.scala | 14 +- .../SocialProviderRegistrySpec.scala | 11 +- .../custom/FacebookProviderSpec.scala | 4 +- .../oauth1/LinkedInProviderSpec.scala | 41 +-- .../oauth1/TwitterProviderSpec.scala | 51 +-- .../providers/oauth1/XingProviderSpec.scala | 41 +-- .../oauth1/secrets/CookieSecretSpec.scala | 30 +- .../services/PlayOAuth1ServiceSpec.scala | 22 +- .../providers/oauth2/Auth0ProviderSpec.scala | 91 +++--- .../oauth2/DropboxProviderSpec.scala | 107 ++++--- .../oauth2/FacebookProviderSpec.scala | 99 +++--- .../oauth2/FoursquareProviderSpec.scala | 123 +++---- .../providers/oauth2/GitHubProviderSpec.scala | 107 ++++--- .../providers/oauth2/GitLabProviderSpec.scala | 99 +++--- .../providers/oauth2/GoogleProviderSpec.scala | 131 ++++---- .../oauth2/InstagramProviderSpec.scala | 99 +++--- .../oauth2/LinkedInProviderSpec.scala | 115 +++---- .../providers/oauth2/VKProviderSpec.scala | 123 +++---- .../providers/openid/SteamProviderSpec.scala | 3 +- .../providers/openid/YahooProviderSpec.scala | 3 +- .../service/PlayOpenIDServiceSpec.scala | 6 +- .../state/CsrfStateItemHandlerSpec.scala | 22 +- .../state/UserStateItemHandlerSpec.scala | 11 +- .../impl/services/GravatarServiceSpec.scala | 54 ++-- .../impl/util/PlayCacheLayerSpec.scala | 14 +- 50 files changed, 1546 insertions(+), 1433 deletions(-) diff --git a/.gitignore b/.gitignore index ec4ccaaf..3fc8d32c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,12 @@ dist /.target /.cache *.DS_Store -.bsp/ + +# Metals +.metals/* +.bloop/* +.vscode/* +**/.bloop/* +**/metals.sbt + +.bsp/* diff --git a/build.sbt b/build.sbt index 39c205ac..423900cf 100644 --- a/build.sbt +++ b/build.sbt @@ -6,6 +6,8 @@ lazy val scala213: String = "2.13.12" lazy val scala3: String = "3.3.1" // Ready for cross build, currently not yet supported by play. lazy val supportedScalaVersions: Seq[String] = Seq(scala213 /*, scala3*/) +Global / evictionErrorLevel := Level.Info + ThisBuild / description := "Authentication library for Play Framework applications that supports several authentication methods, including OAuth1, OAuth2, OpenID, CAS, Credentials, Basic Authentication, Two Factor Authentication or custom authentication schemes" ThisBuild / homepage := Some(url("https://silhouette.readme.io/")) ThisBuild / licenses := Seq("Apache License" -> url("https://github.com/honeycomb-cheesecake/play-silhouette/blob/main/LICENSE")) @@ -99,7 +101,9 @@ lazy val silhouette = (project in file("silhouette")) .settings( name := "play-silhouette", dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"), - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-mock"), + dependencyUpdatesFilter -= moduleFilter(organization = "com.typesafe.akka", name = "akka-testkit"), + dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), + dependencyUpdatesFilter -= moduleFilter(organization = "om.auth0", name = "java-jwt"), libraryDependencies ++= Library.updates ++ Seq( Library.Play.cache, @@ -109,27 +113,28 @@ lazy val silhouette = (project in file("silhouette")) Library.apacheCommonLang, Library.Play.specs2 % Test, Library.Specs2.matcherExtra % Test, - Library.Specs2.mock % Test, + Library.mockito % Test, Library.scalaGuice % Test, Library.akkaTestkit % Test ), resolvers ++= Dependencies.resolvers ) .enablePlugins(PlayScala) + .disablePlugins(PlayAkkaHttpServer) lazy val silhouetteCas = (project in file("silhouette-cas")) .settings( name := "play-silhouette-cas", dependencyUpdatesFailBuild := false, dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"), - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-mock"), + dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), libraryDependencies ++= Library.updates ++ Seq( Library.casClient, Library.casClientSupportSAML, Library.Play.specs2 % Test, Library.Specs2.matcherExtra % Test, - Library.Specs2.mock % Test, + Library.mockito % Test, Library.scalaGuice % Test ) ) @@ -140,6 +145,7 @@ lazy val silhouetteTotp = (project in file("silhouette-totp")) name := "play-silhouette-totp", dependencyUpdatesFailBuild := false, dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"), + dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), libraryDependencies ++= Library.updates ++ Seq( Library.googleAuth, @@ -154,6 +160,9 @@ lazy val silhouetteCryptoJca = (project in file("silhouette-crypto-jca")) dependencyUpdatesFailBuild := false, dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"), dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"), + dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), + dependencyUpdatesFilter -= moduleFilter(organization = "commons-codec", name = "commons-codec"), + libraryDependencies ++= Library.updates ++ Seq( Library.commonsCodec, @@ -168,6 +177,7 @@ lazy val silhouetteArgon2 = (project in file("silhouette-password-argon2")) name := "play-silhouette-password-argon2", dependencyUpdatesFailBuild := false, dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"), + dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), libraryDependencies ++= Library.updates ++ Seq( Library.argon2, @@ -181,6 +191,7 @@ lazy val silhouetteBcrypt = (project in file("silhouette-password-bcrypt")) name := "play-silhouette-password-bcrypt", dependencyUpdatesFailBuild := false, dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"), + dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), libraryDependencies ++= Library.updates ++ Seq( Library.jbcrypt, @@ -195,12 +206,12 @@ lazy val silhouettePersistence = (project in file("silhouette-persistence")) dependencyUpdatesFailBuild := false, dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"), dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"), - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-mock"), + dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), libraryDependencies ++= Library.updates ++ Seq( Library.Specs2.core % Test, Library.Specs2.matcherExtra % Test, - Library.Specs2.mock % Test, + Library.mockito % Test, Library.scalaGuice % Test ) ) @@ -211,13 +222,14 @@ lazy val silhouetteTestkit = (project in file("silhouette-testkit")) name := "play-silhouette-testkit", dependencyUpdatesFailBuild := false, dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"), - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-mock"), + dependencyUpdatesFilter -= moduleFilter(organization = "com.typesafe.akka", name = "akka-testkit"), + dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), libraryDependencies ++= Library.updates ++ Seq( Library.Play.test, Library.Play.specs2 % Test, Library.Specs2.matcherExtra % Test, - Library.Specs2.mock % Test, + Library.mockito % Test, Library.scalaGuice % Test, Library.akkaTestkit % Test ) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 4c8b4267..e0b7a884 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -38,7 +38,6 @@ object Dependencies { private val version = "4.20.2" // Versions later than this will fail due to removed dependencies. val core = "org.specs2" %% "specs2-core" % version val matcherExtra = "org.specs2" %% "specs2-matcher-extra" % version - val mock = "org.specs2" %% "specs2-mock" % version } val argon2 = "de.mkammerer" % "argon2-jvm" % "2.11" @@ -47,6 +46,7 @@ object Dependencies { val jwt = "com.auth0" % "java-jwt" % "3.18.2" val scalaGuice = "net.codingwell" %% "scala-guice" % "6.0.0" val akkaTestkit = "com.typesafe.akka" %% "akka-testkit" % play.core.PlayVersion.akkaVersion + val mockito = "org.mockito" % "mockito-core" % "5.3.0" val casClient = "org.jasig.cas.client" % "cas-client-core" % "3.6.4" val casClientSupportSAML = "org.jasig.cas.client" % "cas-client-support-saml" % "3.6.4" val apacheCommonLang = "org.apache.commons" % "commons-lang3" % "3.13.0" diff --git a/project/plugins.sbt b/project/plugins.sbt index 28b04e59..f7e3e146 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,7 +4,7 @@ addSbtPlugin(dependency = "com.github.sbt" % "sbt-unidoc" % "0.5.0") addSbtPlugin(dependency = "com.jsuereth" % "sbt-pgp" % "2.1.1") addSbtPlugin(dependency = "com.timushev.sbt" % "sbt-updates" % "0.6.4") addSbtPlugin(dependency = "com.typesafe.play" % "sbt-plugin" % "2.9.0") -addSbtPlugin(dependency = "net.vonbuchholtz" % "sbt-dependency-check" % "4.0.0") +addSbtPlugin(dependency = "net.vonbuchholtz" % "sbt-dependency-check" % "5.1.0") addSbtPlugin(dependency = "org.scoverage" % "sbt-scoverage" % "2.0.9") addSbtPlugin(dependency = "org.scoverage" % "sbt-coveralls" % "1.3.11") addSbtPlugin(dependency = "org.xerial.sbt" % "sbt-sonatype" % "3.10.0") diff --git a/silhouette-cas/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/CasProviderSpec.scala b/silhouette-cas/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/CasProviderSpec.scala index 3d0a93b9..924ee00a 100644 --- a/silhouette-cas/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/CasProviderSpec.scala +++ b/silhouette-cas/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/CasProviderSpec.scala @@ -20,7 +20,7 @@ import io.github.honeycombcheesecake.play.silhouette.api.util.HTTPLayer import io.github.honeycombcheesecake.play.silhouette.api.{ Logger, LoginInfo } import org.jasig.cas.client.authentication.AttributePrincipal -import org.specs2.mock.Mockito +import org.mockito.Mockito._ import org.specs2.specification.Scope import play.api.mvc.AnyContentAsEmpty import play.api.test.FakeRequest @@ -33,7 +33,7 @@ import scala.concurrent.duration._ /** * Test case for the [[CasProvider]] class. */ -class CasProviderSpec extends SocialProviderSpec[CasInfo] with Mockito with Logger { +class CasProviderSpec extends SocialProviderSpec[CasInfo] with Logger { "The settings" should { "fail with a ConfigurationException if casURL is invalid" in new Context { @@ -95,9 +95,9 @@ class CasProviderSpec extends SocialProviderSpec[CasInfo] with Mockito with Logg "The `retrieveProfile` method" should { "return a valid profile if the CAS client validates the ticket" in new Context { - principal.getName returns userName - principal.getAttributes returns attr - client.validateServiceTicket(ticket) returns Future.successful(principal) + when(principal.getName).thenReturn(userName) + when(principal.getAttributes).thenReturn(attr) + when(client.validateServiceTicket(ticket)).thenReturn(Future.successful(principal)) implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/?ticket=%s".format(ticket)) @@ -129,8 +129,8 @@ class CasProviderSpec extends SocialProviderSpec[CasInfo] with Mockito with Logg redirectURL = "https://cas-redirect/") lazy val httpLayer = { - val m = mock[HTTPLayer] - m.executionContext returns global + val m = mock(classOf[HTTPLayer]) + when(m.executionContext).thenReturn(global) m } @@ -144,7 +144,7 @@ class CasProviderSpec extends SocialProviderSpec[CasInfo] with Mockito with Logg lazy val casAuthInfo = CasInfo(ticket) - lazy val principal = mock[AttributePrincipal].smart + lazy val principal = mock(classOf[AttributePrincipal], withSettings().defaultAnswer(RETURNS_SMART_NULLS)) lazy val name = "abc123" lazy val email = "email" diff --git a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala index df805c82..781e0f24 100644 --- a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala +++ b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala @@ -19,9 +19,9 @@ import io.github.honeycombcheesecake.play.silhouette.api.StorableAuthenticator import io.github.honeycombcheesecake.play.silhouette.api.util.CacheLayer import io.github.honeycombcheesecake.play.silhouette.test.WaitPatience import org.specs2.concurrent.ExecutionEnv -import org.specs2.mock.Mockito import org.specs2.mutable.Specification import org.specs2.specification.Scope +import org.mockito.Mockito._ import scala.concurrent.Future import scala.concurrent.duration.Duration @@ -29,50 +29,50 @@ import scala.concurrent.duration.Duration /** * Test case for the [[CacheAuthenticatorRepository]] class. */ -class CacheAuthenticatorRepositorySpec(implicit ev: ExecutionEnv) extends Specification with Mockito with WaitPatience { +class CacheAuthenticatorRepositorySpec(implicit ev: ExecutionEnv) extends Specification with WaitPatience { "The `find` method" should { "return value from cache" in new Context { - cacheLayer.find[StorableAuthenticator]("test-id") returns Future.successful(Some(authenticator)) + when(cacheLayer.find[StorableAuthenticator]("test-id")).thenReturn(Future.successful(Some(authenticator))) repository.find("test-id") must beSome(authenticator).awaitWithPatience - there was one(cacheLayer).find[StorableAuthenticator]("test-id") + verify(cacheLayer).find[StorableAuthenticator]("test-id") } "return None if value couldn't be found in cache" in new Context { - cacheLayer.find[StorableAuthenticator]("test-id") returns Future.successful(None) + when(cacheLayer.find[StorableAuthenticator]("test-id")).thenReturn(Future.successful(None)) repository.find("test-id") must beNone.awaitWithPatience - there was one(cacheLayer).find[StorableAuthenticator]("test-id") + verify(cacheLayer).find[StorableAuthenticator]("test-id") } } "The `add` method" should { "add value in cache" in new Context { - authenticator.id returns "test-id" - cacheLayer.save("test-id", authenticator, Duration.Inf) returns Future.successful(authenticator) + when(authenticator.id).thenReturn("test-id") + when(cacheLayer.save("test-id", authenticator, Duration.Inf)).thenReturn(Future.successful(authenticator)) repository.add(authenticator) must beEqualTo(authenticator).awaitWithPatience - there was one(cacheLayer).save("test-id", authenticator, Duration.Inf) + verify(cacheLayer).save("test-id", authenticator, Duration.Inf) } } "The `update` method" should { "update value in cache" in new Context { - authenticator.id returns "test-id" - cacheLayer.save("test-id", authenticator, Duration.Inf) returns Future.successful(authenticator) + when(authenticator.id).thenReturn("test-id") + when(cacheLayer.save("test-id", authenticator, Duration.Inf)).thenReturn(Future.successful(authenticator)) repository.update(authenticator) must beEqualTo(authenticator).awaitWithPatience - there was one(cacheLayer).save("test-id", authenticator, Duration.Inf) + verify(cacheLayer).save("test-id", authenticator, Duration.Inf) } } "The `remove` method" should { "remove value from cache" in new Context { - cacheLayer.remove("test-id") returns Future.successful(()) + when(cacheLayer.remove("test-id")).thenReturn(Future.successful(())) repository.remove("test-id") must beEqualTo(()).awaitWithPatience - there was one(cacheLayer).remove("test-id") + verify(cacheLayer).remove("test-id") } } @@ -84,12 +84,12 @@ class CacheAuthenticatorRepositorySpec(implicit ev: ExecutionEnv) extends Specif /** * A storable authenticator. */ - lazy val authenticator = mock[StorableAuthenticator] + lazy val authenticator = mock(classOf[StorableAuthenticator]) /** * The cache layer implementation. */ - lazy val cacheLayer = mock[CacheLayer] + lazy val cacheLayer = mock(classOf[CacheLayer]) /** * The repository to test. diff --git a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala index eeefc3df..e8c75cbe 100644 --- a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala +++ b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala @@ -27,9 +27,9 @@ import io.github.honeycombcheesecake.play.silhouette.test.WaitPatience import net.codingwell.scalaguice.ScalaModule import org.specs2.concurrent.ExecutionEnv import org.specs2.control.NoLanguageFeatures -import org.specs2.mock.Mockito import org.specs2.mutable.Specification import org.specs2.specification.Scope +import org.mockito.Mockito._ import scala.concurrent.Await import scala.concurrent.duration._ @@ -39,7 +39,7 @@ import scala.language.postfixOps * Test case for the [[DelegableAuthInfoRepository]] trait. */ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv) - extends Specification with Mockito with NoLanguageFeatures with WaitPatience { + extends Specification with NoLanguageFeatures with WaitPatience { "The `find` method" should { "delegate the PasswordInfo to the correct DAO" in new Context { @@ -48,7 +48,7 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv) Await.result(passwordInfoDAO.add(loginInfo, passwordInfo), 10 seconds) service.find[PasswordInfo](loginInfo) must beSome(passwordInfo).awaitWithPatience - there was one(passwordInfoDAO).find(loginInfo) + verify(passwordInfoDAO).find(loginInfo) } "delegate the OAuth1Info to the correct DAO" in new Context { @@ -57,7 +57,7 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv) Await.result(oauth1InfoDAO.add(loginInfo, oauth1Info), 10 seconds) service.find[OAuth1Info](loginInfo) must beSome(oauth1Info).awaitWithPatience - there was one(oauth1InfoDAO).find(loginInfo) + verify(oauth1InfoDAO).find(loginInfo) } "delegate the OAuth2Info to the correct DAO" in new Context { @@ -66,7 +66,7 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv) Await.result(oauth2InfoDAO.add(loginInfo, oauth2Info), 10 seconds) service.find[OAuth2Info](loginInfo) must beSome(oauth2Info).awaitWithPatience - there was one(oauth2InfoDAO).find(loginInfo) + verify(oauth2InfoDAO).find(loginInfo) } "throw a ConfigurationException if an unsupported type was given" in new Context { @@ -83,21 +83,21 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv) val loginInfo = LoginInfo("credentials", "1") service.add(loginInfo, passwordInfo) must beEqualTo(passwordInfo).awaitWithPatience - there was one(passwordInfoDAO).add(loginInfo, passwordInfo) + verify(passwordInfoDAO).add(loginInfo, passwordInfo) } "delegate the OAuth1Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") service.add(loginInfo, oauth1Info) must beEqualTo(oauth1Info).awaitWithPatience - there was one(oauth1InfoDAO).add(loginInfo, oauth1Info) + verify(oauth1InfoDAO).add(loginInfo, oauth1Info) } "delegate the OAuth2Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") service.add(loginInfo, oauth2Info) must beEqualTo(oauth2Info).awaitWithPatience - there was one(oauth2InfoDAO).add(loginInfo, oauth2Info) + verify(oauth2InfoDAO).add(loginInfo, oauth2Info) } "throw a ConfigurationException if an unsupported type was given" in new Context { @@ -114,21 +114,21 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv) val loginInfo = LoginInfo("credentials", "1") service.update(loginInfo, passwordInfo) must beEqualTo(passwordInfo).awaitWithPatience - there was one(passwordInfoDAO).update(loginInfo, passwordInfo) + verify(passwordInfoDAO).update(loginInfo, passwordInfo) } "delegate the OAuth1Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") service.update(loginInfo, oauth1Info) must beEqualTo(oauth1Info).awaitWithPatience - there was one(oauth1InfoDAO).update(loginInfo, oauth1Info) + verify(oauth1InfoDAO).update(loginInfo, oauth1Info) } "delegate the OAuth2Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") service.update(loginInfo, oauth2Info) must beEqualTo(oauth2Info).awaitWithPatience - there was one(oauth2InfoDAO).update(loginInfo, oauth2Info) + verify(oauth2InfoDAO).update(loginInfo, oauth2Info) } "throw a ConfigurationException if an unsupported type was given" in new Context { @@ -145,21 +145,21 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv) val loginInfo = LoginInfo("credentials", "1") service.save(loginInfo, passwordInfo) must beEqualTo(passwordInfo).awaitWithPatience - there was one(passwordInfoDAO).save(loginInfo, passwordInfo) + verify(passwordInfoDAO).save(loginInfo, passwordInfo) } "delegate the OAuth1Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") service.save(loginInfo, oauth1Info) must beEqualTo(oauth1Info).awaitWithPatience - there was one(oauth1InfoDAO).save(loginInfo, oauth1Info) + verify(oauth1InfoDAO).save(loginInfo, oauth1Info) } "delegate the OAuth2Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") service.save(loginInfo, oauth2Info) must beEqualTo(oauth2Info).awaitWithPatience - there was one(oauth2InfoDAO).save(loginInfo, oauth2Info) + verify(oauth2InfoDAO).save(loginInfo, oauth2Info) } "throw a ConfigurationException if an unsupported type was given" in new Context { @@ -178,7 +178,7 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv) Await.result(passwordInfoDAO.add(loginInfo, passwordInfo), 10 seconds) service.remove[PasswordInfo](loginInfo) must beEqualTo(()).awaitWithPatience - there was one(passwordInfoDAO).remove(loginInfo) + verify(passwordInfoDAO).remove(loginInfo) } "delegate the OAuth1Info to the correct DAO" in new Context { @@ -187,7 +187,7 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv) Await.result(oauth1InfoDAO.add(loginInfo, oauth1Info), 10 seconds) service.remove[OAuth1Info](loginInfo) must beEqualTo(()).awaitWithPatience - there was one(oauth1InfoDAO).remove(loginInfo) + verify(oauth1InfoDAO).remove(loginInfo) } "delegate the OAuth2Info to the correct DAO" in new Context { @@ -196,7 +196,7 @@ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv) Await.result(oauth2InfoDAO.add(loginInfo, oauth2Info), 10 seconds) service.remove[OAuth2Info](loginInfo) must beEqualTo(()).awaitWithPatience - there was one(oauth2InfoDAO).remove(loginInfo) + verify(oauth2InfoDAO).remove(loginInfo) } "throw a ConfigurationException if an unsupported type was given" in new Context { diff --git a/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala b/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala index 0812cf18..65f626d9 100644 --- a/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala +++ b/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala @@ -17,7 +17,8 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers import io.github.honeycombcheesecake.play.silhouette.api.util.{ Credentials, PasswordInfo } import com.warrenstrange.googleauth.GoogleAuthenticator -import org.specs2.mock.Mockito +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any import play.api.test.WithApplication import scala.concurrent.ExecutionContext.Implicits.global @@ -25,7 +26,7 @@ import scala.concurrent.ExecutionContext.Implicits.global /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.providers.GoogleTotpProvider#GoogleTOTPProvider]] class. */ -class GoogleTotpProviderSpec extends PasswordProviderSpec with Mockito { +class GoogleTotpProviderSpec extends PasswordProviderSpec { "The `authenticate` with verification code method" should { "return None when the sharedKey is null or empty" in new WithApplication with Context { await(provider.authenticate(null.asInstanceOf[String], testVerificationCode)) should be(None) @@ -51,9 +52,9 @@ class GoogleTotpProviderSpec extends PasswordProviderSpec with Mockito { "The `createCredentials` method" should { "return the correct TotpCredentials shared key" in new WithApplication with Context { val result = provider.createCredentials(credentials.identifier) - result.totpInfo.sharedKey must not be "" - result.totpInfo.scratchCodes must not be empty - result.qrUrl must not be "" + result.totpInfo.sharedKey.nonEmpty must beTrue + result.totpInfo.scratchCodes.nonEmpty must beTrue + result.qrUrl.nonEmpty must beTrue } } @@ -73,8 +74,8 @@ class GoogleTotpProviderSpec extends PasswordProviderSpec with Mockito { } "return Some(PasswordInfo,TotpInfo) when the plain scratch code is valid" in new WithApplication with Context { - fooHasher.hash(any()) returns testPasswordInfo - barHasher.matches(testPasswordInfo, testScratchCode) returns true + when(fooHasher.hash(any())).thenReturn(testPasswordInfo) + when(barHasher.matches(testPasswordInfo, testScratchCode)).thenReturn(true) val result = provider.createCredentials(credentials.identifier) await(provider.authenticate(result.totpInfo, testScratchCode)) should not be empty } diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticator.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticator.scala index e3786b53..0cc93aa0 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticator.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticator.scala @@ -280,7 +280,7 @@ class CookieAuthenticatorService( val combinedCookies = filteredCookies :+ cookie val cookies = Cookies(combinedCookies) - request.withAttrs(request.attrs.updated(RequestAttrKey.Cookies.bindValue(Cell(cookies)))) + request.withAttrs(request.attrs.updated(RequestAttrKey.Cookies, Cell(cookies))) } /** diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticator.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticator.scala index 7112179d..62fddbbe 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticator.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticator.scala @@ -214,7 +214,7 @@ class SessionAuthenticatorService( case None => session } - request.withAttrs(request.attrs.updated(RequestAttrKey.Session.bindValue(Cell(s)))) + request.withAttrs(request.attrs.updated(RequestAttrKey.Session, Cell(s))) } /** diff --git a/silhouette/test/Helpers.scala b/silhouette/test/Helpers.scala index 0684f6f6..45f36c18 100644 --- a/silhouette/test/Helpers.scala +++ b/silhouette/test/Helpers.scala @@ -19,7 +19,6 @@ import io.github.honeycombcheesecake.play.silhouette.api.AuthInfo import io.github.honeycombcheesecake.play.silhouette.impl.providers.{ SocialProfile, SocialStateItem, StatefulAuthInfo } import org.specs2.execute.{ AsResult, Result => Specs2Result } import org.specs2.matcher.{ JsonMatchers, MatchResult } -import org.specs2.mock.Mockito import org.specs2.mutable.Around import play.api.libs.json.{ JsValue, Json } import play.api.mvc.{ Result => PlayResult } @@ -28,6 +27,7 @@ import play.api.test.PlaySpecification import scala.concurrent.Future import scala.io.{ Codec, Source } import scala.reflect.ClassTag +import org.mockito.Mockito /** * Executes a before method in the context of the around method. @@ -63,7 +63,7 @@ trait BeforeAfterWithinAround extends Around { /** * Base test case for the social providers. */ -trait SocialProviderSpec[A <: AuthInfo] extends PlaySpecification with Mockito with JsonMatchers { +trait SocialProviderSpec[A <: AuthInfo] extends PlaySpecification with JsonMatchers { /** * Applies a matcher on a simple result. @@ -121,7 +121,7 @@ trait SocialProviderSpec[A <: AuthInfo] extends PlaySpecification with Mockito w lazy val result = await(providerResult.failed) - result must not[Any](throwAn[E]) + result must not[Throwable](throwAn[E]) result.rethrow must throwAn[E].like(f) } } @@ -177,4 +177,15 @@ object Helper { case None => throw new Exception("Cannot load file: " + file) } } + + /** + * Mock related helpers + */ + + def mock[A](implicit a: ClassTag[A]): A = + Mockito.mock(a.runtimeClass).asInstanceOf[A] + + def mockSmart[A](implicit a: ClassTag[A]): A = + Mockito.mock(a.runtimeClass, Mockito.withSettings().defaultAnswer(Mockito.RETURNS_SMART_NULLS)).asInstanceOf[A] + } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala index 4864c218..29888a75 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala @@ -26,7 +26,6 @@ import io.github.honeycombcheesecake.play.silhouette.api.services.{ Authenticato import net.codingwell.scalaguice.ScalaModule import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.JsonMatchers -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.inject.bind import play.api.inject.guice.GuiceApplicationBuilder @@ -34,6 +33,9 @@ import play.api.libs.json.Json import play.api.mvc.Results._ import play.api.mvc._ import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ +import test.Helper.mock import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -44,13 +46,13 @@ import scala.reflect.ClassTag /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.api.actions.SecuredActionSpec]]. */ -class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers with NoLanguageFeatures { +class SecuredActionSpec extends PlaySpecification with JsonMatchers with NoLanguageFeatures { "The `SecuredAction` action" should { "restrict access if no valid authenticator can be retrieved" in new InjectorContext { new WithApplication(app) with Context { withEvent[NotAuthenticatedEvent] { - env.authenticatorService.retrieve(any()) returns Future.successful(None) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) val result = controller.defaultAction(request) @@ -63,9 +65,9 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "restrict access and discard authenticator if an invalid authenticator can be retrieved" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator.copy(isValid = false))) - env.authenticatorService.discard(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator.copy(isValid = false)))) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } withEvent[NotAuthenticatedEvent] { @@ -73,7 +75,7 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers status(result) must equalTo(UNAUTHORIZED) contentAsString(result) must contain("global.not.authenticated") - there was one(env.authenticatorService).discard(any(), any())(any()) + verify(env.authenticatorService).discard(any(), any())(any()) theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request)) } } @@ -81,18 +83,18 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "restrict access and discard authenticator if no identity could be found for an authenticator" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.discard(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(None) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) withEvent[NotAuthenticatedEvent] { val result = controller.defaultAction(request) status(result) must equalTo(UNAUTHORIZED) contentAsString(result) must contain("global.not.authenticated") - there was one(env.authenticatorService).discard(any(), any())(any()) + verify(env.authenticatorService).discard(any(), any())(any()) theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request)) } } @@ -100,11 +102,11 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "display local not-authenticated result if user isn't authenticated[authorization and error handler]" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.discard(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(None) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) val result = controller.actionWithAuthorizationAndErrorHandler(request) @@ -115,11 +117,11 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "display local not-authenticated result if user isn't authenticated[error handler only]" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.discard(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(None) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) val result = controller.actionWithErrorHandler(request) @@ -130,11 +132,11 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "display global not-authenticated result if user isn't authenticated" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.discard(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(None) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) val result = controller.defaultAction(request) @@ -145,20 +147,20 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "restrict access and update authenticator if a user is authenticated but not authorized" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.update(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) - authorization.isAuthorized(any(), any())(any()) returns Future.successful(false) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + when(authorization.isAuthorized(any(), any())(any())).thenReturn(Future.successful(false)) withEvent[NotAuthorizedEvent[FakeIdentity]] { val result = controller.actionWithAuthorization(request) status(result) must equalTo(FORBIDDEN) contentAsString(result) must contain("global.not.authorized") - there was one(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).update(any(), any())(any()) theProbe.expectMsg(500 millis, NotAuthorizedEvent(identity, request)) } } @@ -166,58 +168,58 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "display local not-authorized result if user isn't authorized" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.update(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) - authorization.isAuthorized(any(), any())(any()) returns Future.successful(false) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + when(authorization.isAuthorized(any(), any())(any())).thenReturn(Future.successful(false)) val result = controller.actionWithAuthorizationAndErrorHandler(request) status(result) must equalTo(FORBIDDEN) contentAsString(result) must contain("local.not.authorized") - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) } } "display global not-authorized result if user isn't authorized" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.update(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) - authorization.isAuthorized(any(), any())(any()) returns Future.successful(false) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + when(authorization.isAuthorized(any(), any())(any())).thenReturn(Future.successful(false)) val result = controller.actionWithAuthorization(request) status(result) must equalTo(FORBIDDEN) contentAsString(result) must contain("global.not.authorized") - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) } } "invoke action without authorization if user is authenticated" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.update(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) withEvent[AuthenticatedEvent[FakeIdentity]] { val result = controller.defaultAction(request) status(result) must equalTo(OK) contentAsString(result) must contain("full.access") - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) } } @@ -225,20 +227,20 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "invoke action with authorization if user is authenticated but not authorized" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.update(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) withEvent[AuthenticatedEvent[FakeIdentity]] { val result = controller.actionWithAuthorization(request) status(result) must equalTo(OK) contentAsString(result) must contain("full.access") - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) } } @@ -246,25 +248,25 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "use next request provider in the chain if first isn't responsible" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - tokenRequestProvider.authenticate(any()) returns Future.successful(None) - basicAuthRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo)) - env.authenticatorService.retrieve(any()) returns Future.successful(None) - env.authenticatorService.create(any())(any()) returns Future.successful(authenticator) - env.authenticatorService.init(any())(any[RequestHeader]()) answers { p: Any => - Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(None)) + when(basicAuthRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.init(any())(any[RequestHeader]())).thenAnswer { p => + Future.successful(p.getArgument(0).asInstanceOf[FakeAuthenticator#Value]) } - env.authenticatorService.embed(any(), any[Result]())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) withEvent[AuthenticatedEvent[FakeIdentity]] { val result = controller.actionWithAuthorization(request) status(result) must equalTo(OK) contentAsString(result) must contain("full.access") - there was one(env.authenticatorService).create(any())(any()) - there was one(env.authenticatorService).init(any())(any()) + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).init(any())(any()) theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) } } @@ -272,11 +274,11 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "update an initialized authenticator if it was touched" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) - env.authenticatorService.update(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } withEvent[AuthenticatedEvent[FakeIdentity]] { @@ -284,8 +286,8 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers status(result) must equalTo(OK) contentAsString(result) must contain("full.access") - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) } } @@ -293,17 +295,17 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "do not update an initialized authenticator if it was not touched" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Right(authenticator) - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Right(authenticator)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) withEvent[AuthenticatedEvent[FakeIdentity]] { val result = controller.actionWithAuthorization(request) status(result) must equalTo(OK) contentAsString(result) must contain("full.access") - there was one(env.authenticatorService).touch(any()) - there was no(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) } } @@ -311,24 +313,24 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "init an uninitialized authenticator" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - tokenRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo)) - env.authenticatorService.retrieve(any()) returns Future.successful(None) - env.authenticatorService.create(any())(any()) returns Future.successful(authenticator) - env.authenticatorService.init(any())(any[RequestHeader]()) answers { p: Any => - Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.init(any())(any[RequestHeader]())).thenAnswer { p => + Future.successful(p.getArgument(0).asInstanceOf[FakeAuthenticator#Value]) } - env.authenticatorService.embed(any(), any[Result]())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) withEvent[AuthenticatedEvent[FakeIdentity]] { val result = controller.actionWithAuthorization(request) status(result) must equalTo(OK) contentAsString(result) must contain("full.access") - there was one(env.authenticatorService).create(any())(any()) - there was one(env.authenticatorService).init(any())(any()) + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).init(any())(any()) theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) } } @@ -336,21 +338,21 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "renew an initialized authenticator" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.renew(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) withEvent[AuthenticatedEvent[FakeIdentity]] { val result = controller.renewAction(request) status(result) must equalTo(OK) contentAsString(result) must contain("renewed") - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).renew(any(), any())(any()) - there was no(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).renew(any(), any())(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) } } @@ -358,21 +360,21 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "renew an uninitialized authenticator" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - tokenRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo)) - env.authenticatorService.retrieve(any()) returns Future.successful(None) - env.authenticatorService.create(any())(any()) returns Future.successful(authenticator) - env.authenticatorService.renew(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) withEvent[AuthenticatedEvent[FakeIdentity]] { val result = controller.renewAction(request) status(result) must equalTo(OK) contentAsString(result) must contain("renewed") - there was one(env.authenticatorService).create(any())(any()) - there was one(env.authenticatorService).renew(any(), any())(any()) + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).renew(any(), any())(any()) theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) } } @@ -380,21 +382,21 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "discard an initialized authenticator" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.discard(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) withEvent[AuthenticatedEvent[FakeIdentity]] { val result = controller.discardAction(request) status(result) must equalTo(OK) contentAsString(result) must contain("discarded") - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).discard(any(), any())(any()) - there was no(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).discard(any(), any())(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) } } @@ -402,20 +404,20 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "discard an uninitialized authenticator" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - tokenRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo)) - env.authenticatorService.retrieve(any()) returns Future.successful(None) - env.authenticatorService.create(any())(any()) returns Future.successful(authenticator) - env.authenticatorService.discard(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) withEvent[AuthenticatedEvent[FakeIdentity]] { val result = controller.discardAction(request) status(result) must equalTo(OK) - there was one(env.authenticatorService).create(any())(any()) - there was one(env.authenticatorService).discard(any(), any())(any()) + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).discard(any(), any())(any()) theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) } } @@ -425,12 +427,12 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers new WithApplication(app) with Context { implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders("Accept" -> "application/json") - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.update(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) withEvent[AuthenticatedEvent[FakeIdentity]] { val result = controller.defaultAction(req) @@ -438,8 +440,8 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers status(result) must equalTo(OK) contentType(result) must beSome("application/json") contentAsString(result) must /("result" -> "full.access") - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, req)) } } @@ -449,31 +451,31 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "The `SecuredRequestHandler`" should { "return status 401 if authentication was not successful" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(None) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) val result = controller.defaultHandler(request) status(result) must equalTo(UNAUTHORIZED) - there was no(env.authenticatorService).touch(any()) - there was no(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService, never()).touch(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) } } "return the user if authentication was successful" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.update(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.defaultHandler(request) status(result) must equalTo(OK) contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "1") - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) } } } @@ -481,9 +483,9 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "The `exceptionHandler` method of the SecuredErrorHandler" should { "translate an ForbiddenException into a 403 Forbidden result" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(None) - env.authenticatorService.discard(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } val failed = Future.failed(new NotAuthorizedException("Access denied")) @@ -495,9 +497,9 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers "translate an UnauthorizedException into a 401 Unauthorized result" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(None) - env.authenticatorService.discard(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } val failed = Future.failed(new NotAuthenticatedException("Not authenticated")) @@ -527,7 +529,7 @@ class SecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers */ lazy val authorization = { val a = mock[Authorization[SecuredEnv#I, SecuredEnv#A]] - a.isAuthorized(any(), any())(any()) returns Future.successful(true) + when(a.isAuthorized(any(), any())(any())).thenReturn(Future.successful(true)) a } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala index 36ca80a7..ee73b1c4 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala @@ -26,13 +26,15 @@ import io.github.honeycombcheesecake.play.silhouette.api.services.{ Authenticato import net.codingwell.scalaguice.ScalaModule import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.JsonMatchers -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.inject.bind import play.api.inject.guice.GuiceApplicationBuilder import play.api.mvc.Results._ import play.api.mvc._ import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ +import test.Helper.mock import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -41,12 +43,12 @@ import scala.reflect.ClassTag /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.api.actions.UnsecuredActionSpec]]. */ -class UnsecuredActionSpec extends PlaySpecification with Mockito with JsonMatchers with NoLanguageFeatures { +class UnsecuredActionSpec extends PlaySpecification with JsonMatchers with NoLanguageFeatures { "The `UnsecuredAction` action" should { "grant access if no valid authenticator can be retrieved" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any) returns Future.successful(None) + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(None)) val result = controller.defaultAction(request) @@ -57,43 +59,43 @@ class UnsecuredActionSpec extends PlaySpecification with Mockito with JsonMatche "grant access and discard authenticator if an invalid authenticator can be retrieved" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any) returns Future.successful(Some(authenticator.copy(isValid = false))) - env.authenticatorService.discard(any, any)(any) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator.copy(isValid = false)))) + when(env.authenticatorService.discard(any, any)(any)).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } val result = controller.defaultAction(request) status(result) must equalTo(OK) contentAsString(result) must contain("full.access") - there was one(env.authenticatorService).discard(any, any)(any) + verify(env.authenticatorService).discard(any, any)(any) } } "grant access and discard authenticator if no identity could be found for an authenticator" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any) returns Future.successful(Some(authenticator)) - env.authenticatorService.discard(any, any)(any) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.discard(any, any)(any)).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(None) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) val result = controller.defaultAction(request) status(result) must equalTo(OK) contentAsString(result) must contain("full.access") - there was one(env.authenticatorService).discard(any, any)(any) + verify(env.authenticatorService).discard(any, any)(any) } } "display local not-authorized result if user is authenticated" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any) returns Left(authenticator) - env.authenticatorService.update(any, any)(any) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any)).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any, any)(any)).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.actionWithErrorHandler(request) @@ -104,12 +106,12 @@ class UnsecuredActionSpec extends PlaySpecification with Mockito with JsonMatche "display global not-authorized result if user is authenticated" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any) returns Left(authenticator) - env.authenticatorService.update(any, any)(any) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any)).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any, any)(any)).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.defaultAction(request) @@ -122,31 +124,31 @@ class UnsecuredActionSpec extends PlaySpecification with Mockito with JsonMatche "The `UnsecuredRequestHandler`" should { "return status 403 if user is authenticated" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any) returns Left(authenticator) - env.authenticatorService.update(any, any)(any) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any)).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any, any)(any)).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.defaultHandler(request) status(result) must equalTo(FORBIDDEN) - there was one(env.authenticatorService).touch(any) - there was one(env.authenticatorService).update(any, any)(any) + verify(env.authenticatorService).touch(any) + verify(env.authenticatorService).update(any, any)(any) } } "return the data if user is not authenticated" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any) returns Future.successful(None) + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(None)) val result = controller.defaultHandler(request) status(result) must equalTo(OK) contentAsString(result) must be equalTo "data" - there was no(env.authenticatorService).touch(any) - there was no(env.authenticatorService).update(any, any)(any) + verify(env.authenticatorService, never()).touch(any) + verify(env.authenticatorService, never()).update(any, any)(any) } } } @@ -154,9 +156,9 @@ class UnsecuredActionSpec extends PlaySpecification with Mockito with JsonMatche "The `exceptionHandler` method of the UnsecuredErrorHandler" should { "translate an ForbiddenException into a 403 Forbidden result" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any) returns Future.successful(None) - env.authenticatorService.discard(any, any)(any) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(None)) + when(env.authenticatorService.discard(any, any)(any)).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } val failed = Future.failed(new NotAuthorizedException("Access denied")) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala index 0f20705c..1140f738 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala @@ -23,13 +23,15 @@ import io.github.honeycombcheesecake.play.silhouette.api.services.{ Authenticato import net.codingwell.scalaguice.ScalaModule import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.JsonMatchers -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.i18n.{ Lang, Langs, MessagesApi } import play.api.inject.guice.GuiceApplicationBuilder import play.api.libs.json.Json import play.api.mvc.{ ControllerComponents, _ } import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ +import test.Helper.mock import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -37,12 +39,12 @@ import scala.concurrent.Future /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.api.actions.UserAwareAction]]. */ -class UserAwareActionSpec extends PlaySpecification with Mockito with JsonMatchers with NoLanguageFeatures { +class UserAwareActionSpec extends PlaySpecification with JsonMatchers with NoLanguageFeatures { "The `UserAwareAction` action" should { "invoke action without identity and authenticator if no authenticator could be found" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(None) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) val result = controller.defaultAction(request) @@ -53,201 +55,201 @@ class UserAwareActionSpec extends PlaySpecification with Mockito with JsonMatche "invoke action without identity and authenticator if invalid authenticator was found" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator.copy(isValid = false))) - env.authenticatorService.discard(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator.copy(isValid = false)))) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } val result = controller.defaultAction(request) status(result) must equalTo(OK) contentAsString(result) must contain(messagesApi("without.identity.and.authenticator")) - there was one(env.authenticatorService).discard(any(), any())(any()) + verify(env.authenticatorService).discard(any(), any())(any()) } } "invoke action with valid authenticator if no identity could be found" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.update(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(None) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) val result = controller.defaultAction(request) status(result) must equalTo(OK) contentAsString(result) must contain(messagesApi("without.identity.and.with.authenticator")) - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) } } "invoke action with authenticator and identity" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.update(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.defaultAction(request) status(result) must equalTo(OK) contentAsString(result) must contain(messagesApi("with.identity.and.authenticator")) - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) } } "use next request provider in the chain if first isn't responsible" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - tokenRequestProvider.authenticate(any()) returns Future.successful(None) - basicAuthRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo)) - env.authenticatorService.retrieve(any()) returns Future.successful(None) - env.authenticatorService.create(any())(any()) returns Future.successful(authenticator) - env.authenticatorService.init(any())(any()) answers { p: Any => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) } - env.authenticatorService.embed(any(), any[Result]())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(None)) + when(basicAuthRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.init(any())(any())).thenAnswer { p: Any => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) } + when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.defaultAction(request) status(result) must equalTo(OK) contentAsString(result) must contain("with.identity.and.authenticator") - there was one(env.authenticatorService).create(any())(any()) - there was one(env.authenticatorService).init(any())(any()) + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).init(any())(any()) } } "update an initialized authenticator if it was touched" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) - env.authenticatorService.update(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } val result = controller.defaultAction(request) status(result) must equalTo(OK) contentAsString(result) must contain("with.identity.and.authenticator") - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) } } "do not update an initialized authenticator if it was not touched" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Right(authenticator) - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Right(authenticator)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.defaultAction(request) status(result) must equalTo(OK) contentAsString(result) must contain(messagesApi("with.identity.and.authenticator")) - there was one(env.authenticatorService).touch(any()) - there was no(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) } } "init an uninitialized authenticator" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - tokenRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo)) - env.authenticatorService.retrieve(any()) returns Future.successful(None) - env.authenticatorService.create(any())(any()) returns Future.successful(authenticator) - env.authenticatorService.init(any())(any()) answers { p: Any => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) } - env.authenticatorService.embed(any(), any[Result]())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.init(any())(any())).thenAnswer { p: Any => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) } + when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.defaultAction(request) status(result) must equalTo(OK) contentAsString(result) must contain("with.identity.and.authenticator") - there was one(env.authenticatorService).create(any())(any()) - there was one(env.authenticatorService).init(any())(any()) + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).init(any())(any()) } } "renew an initialized authenticator" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.renew(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.renewAction(request) status(result) must equalTo(OK) contentAsString(result) must contain(messagesApi("renewed")) - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).renew(any(), any())(any()) - there was no(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).renew(any(), any())(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) } } "renew an uninitialized authenticator" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - tokenRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo)) - env.authenticatorService.retrieve(any()) returns Future.successful(None) - env.authenticatorService.create(any())(any()) returns Future.successful(authenticator) - env.authenticatorService.renew(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.renewAction(request) status(result) must equalTo(OK) contentAsString(result) must contain("renewed") - there was one(env.authenticatorService).create(any())(any()) - there was one(env.authenticatorService).renew(any(), any())(any()) + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).renew(any(), any())(any()) } } "discard an initialized authenticator" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.discard(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.discardAction(request) status(result) must equalTo(OK) contentAsString(result) must contain(messagesApi("discarded")) - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).discard(any(), any())(any()) - there was no(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).discard(any(), any())(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) } } "discard an uninitialized authenticator" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - tokenRequestProvider.authenticate(any()) returns Future.successful(Some(identity.loginInfo)) - env.authenticatorService.retrieve(any()) returns Future.successful(None) - env.authenticatorService.create(any())(any()) returns Future.successful(authenticator) - env.authenticatorService.discard(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.discardAction(request) status(result) must equalTo(OK) - there was one(env.authenticatorService).create(any())(any()) - there was one(env.authenticatorService).discard(any(), any())(any()) + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).discard(any(), any())(any()) } } } @@ -255,31 +257,31 @@ class UserAwareActionSpec extends PlaySpecification with Mockito with JsonMatche "The `UserAwareRequestHandler`" should { "return status 401 if authentication was not successful" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(None) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) val result = controller.defaultHandler(request) status(result) must equalTo(UNAUTHORIZED) - there was no(env.authenticatorService).touch(any()) - there was no(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService, never()).touch(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) } } "return the user if authentication was successful" in new InjectorContext { new WithApplication(app) with Context { - env.authenticatorService.retrieve(any()) returns Future.successful(Some(authenticator)) - env.authenticatorService.touch(any()) returns Left(authenticator) - env.authenticatorService.update(any(), any())(any()) answers { (a, m) => - Future.successful(AuthenticatorResult(a.asInstanceOf[Array[Any]](1).asInstanceOf[Result])) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } - env.identityService.retrieve(identity.loginInfo) returns Future.successful(Some(identity)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) val result = controller.defaultHandler(request) status(result) must equalTo(OK) contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "1") - there was one(env.authenticatorService).touch(any()) - there was one(env.authenticatorService).update(any(), any())(any()) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala index 05284333..6789a9ad 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala @@ -23,10 +23,12 @@ import io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorS import io.github.honeycombcheesecake.play.silhouette.api.util.{ RequestPart, Clock, IDGenerator } import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.BearerTokenAuthenticatorService._ import org.specs2.control.NoLanguageFeatures -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.mvc.{ Results, AnyContentAsEmpty } import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any +import test.Helper.mockSmart import java.time.ZonedDateTime import scala.concurrent.ExecutionContext.Implicits.global @@ -37,7 +39,7 @@ import scala.language.postfixOps /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.BearerTokenAuthenticator]]. */ -class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with NoLanguageFeatures { +class BearerTokenAuthenticatorSpec extends PlaySpecification with NoLanguageFeatures { "The `isValid` method of the authenticator" should { "return false if the authenticator is expired" in new Context { @@ -61,8 +63,8 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val id = "test-id" - idGenerator.generate returns Future.successful(id) - clock.now returns ZonedDateTime.now + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(ZonedDateTime.now) await(service.create(loginInfo)).id must be equalTo id } @@ -71,8 +73,8 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val now = ZonedDateTime.now - idGenerator.generate returns Future.successful("test-id") - clock.now returns now + when(idGenerator.generate).thenReturn(Future.successful("test-id")) + when(clock.now).thenReturn(now) await(service.create(loginInfo)).lastUsedDateTime must be equalTo now } @@ -81,8 +83,8 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val now = ZonedDateTime.now - idGenerator.generate returns Future.successful("test-id") - clock.now returns now + when(idGenerator.generate).thenReturn(Future.successful("test-id")) + when(clock.now).thenReturn(now) await(service.create(loginInfo)).expirationDateTime must be equalTo now + 12.hours } @@ -92,9 +94,9 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N val sixHours = 6 hours val now = ZonedDateTime.now - settings.authenticatorExpiry returns sixHours - idGenerator.generate returns Future.successful("test-id") - clock.now returns now + when(settings.authenticatorExpiry).thenReturn(sixHours) + when(idGenerator.generate).thenReturn(Future.successful("test-id")) + when(clock.now).thenReturn(now) await(service.create(loginInfo)).expirationDateTime must be equalTo now + sixHours } @@ -102,7 +104,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N "throws an AuthenticatorCreationException exception if an error occurred during creation" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - idGenerator.generate returns Future.failed(new Exception("Could not generate ID")) + when(idGenerator.generate).thenReturn(Future.failed(new Exception("Could not generate ID"))) await(service.create(loginInfo)) must throwA[AuthenticatorCreationException].like { case e => @@ -121,7 +123,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N "return None if no authenticator is stored for the token located in the headers" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> authenticator.id) - repository.find(authenticator.id) returns Future.successful(None) + when(repository.find(authenticator.id)).thenReturn(Future.successful(None)) await(service.retrieve) must beNone } @@ -129,7 +131,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N "return authenticator if an authenticator is stored for token located in the header" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> authenticator.id) - repository.find(authenticator.id) returns Future.successful(Some(authenticator)) + when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator))) await(service.retrieve) must beSome(authenticator) } @@ -137,8 +139,8 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N "return authenticator if an authenticator is stored for the token located in the query string" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest("GET", s"?${settings.fieldName}=${authenticator.id}") - settings.requestParts returns Some(Seq(RequestPart.QueryString)) - repository.find(authenticator.id) returns Future.successful(Some(authenticator)) + when(settings.requestParts).thenReturn(Some(Seq(RequestPart.QueryString))) + when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator))) await(service.retrieve) must beSome(authenticator) } @@ -146,7 +148,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N "throws an AuthenticatorRetrievalException exception if an error occurred during retrieval" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> authenticator.id) - repository.find(authenticator.id) returns Future.failed(new RuntimeException("Cannot find authenticator")) + when(repository.find(authenticator.id)).thenReturn(Future.failed(new RuntimeException("Cannot find authenticator"))) await(service.retrieve) must throwA[AuthenticatorRetrievalException].like { case e => @@ -157,17 +159,17 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N "The `init` method of the service" should { "save the authenticator in backing store" in new Context { - repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) } + when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[BearerTokenAuthenticator]) } implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val token = await(service.init(authenticator)) token must be equalTo authenticator.id - there was one(repository).add(authenticator) + verify(repository).add(authenticator) } "throws an AuthenticatorInitializationException exception if an error occurred during initialization" in new Context { - repository.add(any()) returns Future.failed(new Exception("Cannot store authenticator")) + when(repository.add(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator"))) implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() @@ -222,8 +224,8 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N "The `touch` method of the service" should { "update the last used date if idle timeout is defined" in new WithApplication with Context { - settings.authenticatorIdleTimeout returns Some(1 second) - clock.now returns ZonedDateTime.now + when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second)) + when(clock.now).thenReturn(ZonedDateTime.now) service.touch(authenticator) must beLeft[BearerTokenAuthenticator].like { case a => @@ -232,8 +234,8 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N } "do not update the last used date if idle timeout is not defined" in new WithApplication with Context { - settings.authenticatorIdleTimeout returns None - clock.now returns ZonedDateTime.now + when(settings.authenticatorIdleTimeout).thenReturn(None) + when(clock.now).thenReturn(ZonedDateTime.now) service.touch(authenticator) must beRight[BearerTokenAuthenticator].like { case a => @@ -244,17 +246,17 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N "The `update` method of the service" should { "update the authenticator in backing store" in new Context { - repository.update(any()) answers { _: Any => Future.successful(authenticator) } + when(repository.update(any())).thenAnswer { _ => Future.successful(authenticator) } implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() await(service.update(authenticator, Results.Ok)) - there was one(repository).update(authenticator) + verify(repository).update(authenticator) } "return the result if the authenticator could be stored in backing store" in new Context { - repository.update(any()) answers { p: Any => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) } + when(repository.update(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[BearerTokenAuthenticator]) } implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val result = service.update(authenticator, Results.Ok) @@ -263,7 +265,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N } "throws an AuthenticatorUpdateException exception if an error occurred during update" in new Context { - repository.update(any()) returns Future.failed(new Exception("Cannot store authenticator")) + when(repository.update(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator"))) implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() @@ -280,14 +282,14 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N val now = ZonedDateTime.now val id = "new-test-id" - repository.remove(authenticator.id) returns Future.successful(()) - repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) } - idGenerator.generate returns Future.successful(id) - clock.now returns now + when(repository.remove(authenticator.id)).thenReturn(Future.successful(())) + when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[BearerTokenAuthenticator]) } + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(now) await(service.renew(authenticator, Results.Ok)) - there was one(repository).remove(authenticator.id) + verify(repository).remove(authenticator.id) } "renew the authenticator and return the response with a new bearer token" in new Context { @@ -295,10 +297,10 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N val now = ZonedDateTime.now val id = "new-test-id" - repository.remove(any()) returns Future.successful(()) - repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) } - idGenerator.generate returns Future.successful(id) - clock.now returns now + when(repository.remove(any())).thenReturn(Future.successful(())) + when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[BearerTokenAuthenticator]) } + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(now) val result = service.renew(authenticator, Results.Ok) @@ -310,10 +312,10 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N val now = ZonedDateTime.now val id = "new-test-id" - repository.remove(any()) returns Future.successful(()) - repository.add(any()) returns Future.failed(new Exception("Cannot store authenticator")) - idGenerator.generate returns Future.successful(id) - clock.now returns now + when(repository.remove(any())).thenReturn(Future.successful(())) + when(repository.add(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator"))) + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(now) await(service.renew(authenticator, Results.Ok)) must throwA[AuthenticatorRenewalException].like { case e => @@ -326,18 +328,18 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N "remove authenticator from backing store" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - repository.remove(authenticator.id) returns Future.successful(authenticator) + when(repository.remove(authenticator.id)).thenReturn(Future.successful(authenticator)) await(service.discard(authenticator, Results.Ok)) - there was one(repository).remove(authenticator.id) + verify(repository).remove(authenticator.id) } "throws an AuthenticatorDiscardingException exception if an error occurred during discarding" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val okResult = Results.Ok - repository.remove(authenticator.id) returns Future.failed(new Exception("Cannot remove authenticator")) + when(repository.remove(authenticator.id)).thenReturn(Future.failed(new Exception("Cannot remove authenticator"))) await(service.discard(authenticator, okResult)) must throwA[AuthenticatorDiscardingException].like { case e => @@ -354,17 +356,17 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N /** * The repository implementation. */ - lazy val repository = mock[AuthenticatorRepository[BearerTokenAuthenticator]].smart + lazy val repository = mockSmart[AuthenticatorRepository[BearerTokenAuthenticator]] /** * The ID generator implementation. */ - lazy val idGenerator = mock[IDGenerator].smart + lazy val idGenerator = mockSmart[IDGenerator] /** * The clock implementation. */ - lazy val clock = mock[Clock].smart + lazy val clock = mockSmart[Clock] /** * The settings. diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala index 91837f5e..1d8f47d1 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala @@ -28,10 +28,12 @@ import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.CookieA import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.CookieAuthenticatorService._ import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.MatchResult -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.mvc.{ AnyContentAsEmpty, Cookie, DefaultCookieHeaderEncoding, Results } import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any +import test.Helper.mockSmart import java.time.ZonedDateTime import scala.concurrent.ExecutionContext.Implicits.global @@ -43,7 +45,7 @@ import scala.util.{ Failure, Success } /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.CookieAuthenticator]]. */ -class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLanguageFeatures { +class CookieAuthenticatorSpec extends PlaySpecification with NoLanguageFeatures { "The `isValid` method of the authenticator" should { "return false if the authenticator is expired" in new Context { @@ -66,13 +68,13 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "sign the cookie" in new WithApplication with Context { serialize(authenticator, signer, authenticatorEncoder) - there was one(signer).sign(any()) + verify(signer).sign(any()) } "encode the cookie" in new WithApplication with Context { serialize(authenticator, signer, authenticatorEncoder) - there was one(authenticatorEncoder).encode(any()) + verify(authenticatorEncoder).encode(any()) } } @@ -95,7 +97,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang val value = "value" val msg = "^" + Pattern.quote(InvalidCookieSignature.format(ID, "")) + ".*" - signer.extract(any()) returns Failure(new Exception("invalid")) + when(signer.extract(any())).thenReturn(Failure(new Exception("invalid"))) unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg) } @@ -113,10 +115,10 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "return a fingerprinted authenticator" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - idGenerator.generate returns Future.successful("test-id") - clock.now returns ZonedDateTime.now - fingerprintGenerator.generate(any()) returns "test" - settings.useFingerprinting returns true + when(idGenerator.generate).thenReturn(Future.successful("test-id")) + when(clock.now).thenReturn(ZonedDateTime.now) + when(fingerprintGenerator.generate(any())).thenReturn("test") + when(settings.useFingerprinting).thenReturn(true) await(service(Some(repository)).create(loginInfo)).fingerprint must beSome("test") } @@ -124,9 +126,9 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "return a non fingerprinted authenticator" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - idGenerator.generate returns Future.successful("test-id") - clock.now returns ZonedDateTime.now - settings.useFingerprinting returns false + when(idGenerator.generate).thenReturn(Future.successful("test-id")) + when(clock.now).thenReturn(ZonedDateTime.now) + when(settings.useFingerprinting).thenReturn(false) await(service(Some(repository)).create(loginInfo)).fingerprint must beNone } @@ -135,8 +137,8 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val id = "test-id" - idGenerator.generate returns Future.successful(id) - clock.now returns ZonedDateTime.now + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(ZonedDateTime.now) await(service(Some(repository)).create(loginInfo)).id must be equalTo id } @@ -145,8 +147,8 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val now = ZonedDateTime.now - idGenerator.generate returns Future.successful("test-id") - clock.now returns now + when(idGenerator.generate).thenReturn(Future.successful("test-id")) + when(clock.now).thenReturn(now) await(service(Some(repository)).create(loginInfo)).lastUsedDateTime must be equalTo now } @@ -155,8 +157,8 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val now = ZonedDateTime.now - idGenerator.generate returns Future.successful("test-id") - clock.now returns now + when(idGenerator.generate).thenReturn(Future.successful("test-id")) + when(clock.now).thenReturn(now) await(service(Some(repository)).create(loginInfo)).expirationDateTime must be equalTo now + 12.hours } @@ -166,9 +168,9 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang val sixHours = 6 hours val now = ZonedDateTime.now - settings.authenticatorExpiry returns sixHours - idGenerator.generate returns Future.successful("test-id") - clock.now returns now + when(settings.authenticatorExpiry).thenReturn(sixHours) + when(idGenerator.generate).thenReturn(Future.successful("test-id")) + when(clock.now).thenReturn(now) await(service(Some(repository)).create(loginInfo)).expirationDateTime must be equalTo now + sixHours } @@ -176,7 +178,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "throws an AuthenticatorCreationException exception if an error occurred during creation" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - idGenerator.generate returns Future.failed(new Exception("Could not generate ID")) + when(idGenerator.generate).thenReturn(Future.failed(new Exception("Could not generate ID"))) await(service(Some(repository)).create(loginInfo)) must throwA[AuthenticatorCreationException].like { case e => @@ -195,7 +197,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "[stateful] return None if no authenticator for the cookie is stored in backing store" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticator.id)) - repository.find(authenticator.id) returns Future.successful(None) + when(repository.find(authenticator.id)).thenReturn(Future.successful(None)) await(service(Some(repository)).retrieve) must beNone } @@ -204,68 +206,68 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticatorEncoder.encode("invalid"))) await(service(None).retrieve) must beNone - there was no(repository).find(any()) + verify(repository, never()).find(any()) } "[stateful] return None if authenticator fingerprint doesn't match current fingerprint" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticator.id)) - fingerprintGenerator.generate(any()) returns "false" - settings.useFingerprinting returns true - authenticator.fingerprint returns Some("test") - repository.find(authenticator.id) returns Future.successful(Some(authenticator)) + when(fingerprintGenerator.generate(any())).thenReturn("false") + when(settings.useFingerprinting).thenReturn(true) + when(authenticator.fingerprint).thenReturn(Some("test")) + when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator))) await(service(Some(repository)).retrieve) must beNone } "[stateless] return None if authenticator fingerprint doesn't match current fingerprint" in new WithApplication with Context { - fingerprintGenerator.generate(any()) returns "false" - settings.useFingerprinting returns true - authenticator.fingerprint returns Some("test") + when(fingerprintGenerator.generate(any())).thenReturn("false") + when(settings.useFingerprinting).thenReturn(true) + when(authenticator.fingerprint).thenReturn(Some("test")) implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder))) await(service(None).retrieve) must beNone - there was no(repository).find(any()) + verify(repository, never()).find(any()) } "[stateful] return authenticator if authenticator fingerprint matches current fingerprint" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticator.id)) - fingerprintGenerator.generate(any()) returns "test" - settings.useFingerprinting returns true - authenticator.fingerprint returns Some("test") - repository.find(authenticator.id) returns Future.successful(Some(authenticator)) + when(fingerprintGenerator.generate(any())).thenReturn("test") + when(settings.useFingerprinting).thenReturn(true) + when(authenticator.fingerprint).thenReturn(Some("test")) + when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator))) await(service(Some(repository)).retrieve) must beSome(authenticator) } "[stateless] return authenticator if authenticator fingerprint matches current fingerprint" in new WithApplication with Context { - fingerprintGenerator.generate(any()) returns "test" - settings.useFingerprinting returns true - authenticator.fingerprint returns Some("test") + when(fingerprintGenerator.generate(any())).thenReturn("test") + when(settings.useFingerprinting).thenReturn(true) + when(authenticator.fingerprint).thenReturn(Some("test")) implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder))) await(service(None).retrieve) must beSome(authenticator) - there was no(repository).find(any()) + verify(repository, never()).find(any()) } "[stateful] return authenticator if fingerprinting is disabled" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticator.id)) - settings.useFingerprinting returns false - repository.find(authenticator.id) returns Future.successful(Some(authenticator)) + when(settings.useFingerprinting).thenReturn(false) + when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator))) await(service(Some(repository)).retrieve) must beSome(authenticator) } "[stateless] return authenticator if fingerprinting is disabled" in new WithApplication with Context { - settings.useFingerprinting returns false + when(settings.useFingerprinting).thenReturn(false) implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder))) - repository.find(authenticator.id) returns Future.successful(Some(authenticator)) + when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator))) await(service(None).retrieve) must beSome(authenticator) } @@ -273,9 +275,9 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "throws an AuthenticatorRetrievalException exception if an error occurred during retrieval" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticator.id)) - fingerprintGenerator.generate(any()) throws new RuntimeException("Could not generate fingerprint") - settings.useFingerprinting returns true - repository.find(authenticator.id) returns Future.successful(Some(authenticator)) + when(fingerprintGenerator.generate(any())).thenThrow(new RuntimeException("Could not generate fingerprint")) + when(settings.useFingerprinting).thenReturn(true) + when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator))) await(service(Some(repository)).retrieve) must throwA[AuthenticatorRetrievalException].like { case e => @@ -286,12 +288,12 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "The `init` method of the service" should { "[stateful] return a cookie with the authenticator ID if the authenticator could be saved in backing store" in new Context { - repository.add(any()) answers { _: Any => Future.successful(authenticator) } + when(repository.add(any())).thenAnswer { _ => Future.successful(authenticator) } implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() await(service(Some(repository)).init(authenticator)) must be equalTo statefulCookie - there was one(repository).add(any()) + verify(repository).add(any()) } "[stateless] return a cookie with a serialized authenticator" in new WithApplication with Context { @@ -300,11 +302,11 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang val cookie = await(service(None).init(authenticator)) unserialize(cookie.value, signer, authenticatorEncoder) must be equalTo unserialize(statelessCookie.value, signer, authenticatorEncoder) - there was no(repository).add(any()) + verify(repository, never()).add(any()) } "throws an AuthenticatorInitializationException exception if an error occurred during initialization" in new Context { - repository.add(any()) returns Future.failed(new Exception("Cannot store authenticator")) + when(repository.add(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator"))) implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() @@ -358,8 +360,8 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "The `touch` method of the service" should { "update the last used date if idle timeout is defined" in new WithApplication with Context { - settings.authenticatorIdleTimeout returns Some(1 second) - clock.now returns ZonedDateTime.now + when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second)) + when(clock.now).thenReturn(ZonedDateTime.now) service(Some(repository)).touch(authenticator) must beLeft[CookieAuthenticator].like { case a => @@ -368,8 +370,8 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang } "do not update the last used date if idle timeout is not defined" in new WithApplication with Context { - settings.authenticatorIdleTimeout returns None - clock.now returns ZonedDateTime.now + when(settings.authenticatorIdleTimeout).thenReturn(None) + when(clock.now).thenReturn(ZonedDateTime.now) service(Some(repository)).touch(authenticator) must beRight[CookieAuthenticator].like { case a => @@ -380,17 +382,17 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "The `update` method of the service" should { "[stateful] update the authenticator in backing store" in new Context { - repository.update(any()) answers { _: Any => Future.successful(authenticator) } + when(repository.update(any())).thenAnswer { _ => Future.successful(authenticator) } implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() await(service(Some(repository)).update(authenticator, Results.Ok)) - there was one(repository).update(authenticator) + verify(repository).update(authenticator) } "[stateful] return the result if the authenticator could be stored in backing store" in new Context { - repository.update(any()) answers { p: Any => Future.successful(p.asInstanceOf[CookieAuthenticator]) } + when(repository.update(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[CookieAuthenticator]) } implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val result = service(Some(repository)).update(authenticator, Results.Ok) @@ -404,11 +406,11 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang status(result) must be equalTo OK cookies(result).get(settings.cookieName) should beSome[Cookie].which(statelessResponseCookieMatcher(authenticator)) - there was no(repository).update(authenticator) + verify(repository, never()).update(authenticator) } "throws an AuthenticatorUpdateException exception if an error occurred during update" in new Context { - repository.update(any()) returns Future.failed(new Exception("Cannot store authenticator")) + when(repository.update(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator"))) implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() @@ -425,14 +427,14 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang val now = ZonedDateTime.now val id = "new-test-id" - repository.remove(authenticator.id) returns Future.successful(()) - repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[CookieAuthenticator]) } - idGenerator.generate returns Future.successful(id) - clock.now returns now + when(repository.remove(authenticator.id)).thenReturn(Future.successful(())) + when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[CookieAuthenticator]) } + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(now) await(service(Some(repository)).renew(authenticator, Results.Ok)) - there was one(repository).remove(authenticator.id) + verify(repository).remove(authenticator.id) } "[stateful] renew the authenticator and return the response with the updated cookie value" in new Context { @@ -440,15 +442,15 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang val now = ZonedDateTime.now val id = "new-test-id" - repository.remove(any()) returns Future.successful(()) - repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[CookieAuthenticator]) } - idGenerator.generate returns Future.successful(id) - clock.now returns now + when(repository.remove(any())).thenReturn(Future.successful(())) + when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[CookieAuthenticator]) } + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(now) val result = service(Some(repository)).renew(authenticator, Results.Ok) cookies(result).get(settings.cookieName) should beSome[Cookie].which(statefulResponseCookieMatcher(id)) - there was one(repository).add(any()) + verify(repository).add(any()) } "[stateless] renew the authenticator and return the response with the updated cookie value" in new WithApplication with Context { @@ -456,15 +458,15 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang val now = ZonedDateTime.now val id = "new-test-id" - settings.useFingerprinting returns false - idGenerator.generate returns Future.successful(id) - clock.now returns now + when(settings.useFingerprinting).thenReturn(false) + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(now) val result = service(None).renew(authenticator, Results.Ok) cookies(result).get(settings.cookieName) should beSome[Cookie].which(statelessResponseCookieMatcher( authenticator.copy(id = id, lastUsedDateTime = now, expirationDateTime = now + settings.authenticatorExpiry))) - there was no(repository).add(any()) + verify(repository, never()).add(any()) } "throws an AuthenticatorRenewalException exception if an error occurred during renewal" in new Context { @@ -472,10 +474,10 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang val now = ZonedDateTime.now val id = "new-test-id" - repository.remove(any()) returns Future.successful(()) - repository.add(any()) returns Future.failed(new Exception("Cannot store authenticator")) - idGenerator.generate returns Future.successful(id) - clock.now returns now + when(repository.remove(any())).thenReturn(Future.successful(())) + when(repository.add(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator"))) + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(now) await(service(Some(repository)).renew(authenticator, Results.Ok)) must throwA[AuthenticatorRenewalException].like { case e => @@ -488,7 +490,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "[stateful] discard the cookie from response and remove it from backing store" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - repository.remove(any()) returns Future.successful(()) + when(repository.remove(any())).thenReturn(Future.successful(())) val result = service(Some(repository)).discard(authenticator, Results.Ok.withCookies(statefulCookie)) @@ -500,7 +502,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang c.domain must be equalTo settings.cookieDomain c.secure must be equalTo settings.secureCookie } - there was one(repository).remove(authenticator.id) + verify(repository).remove(authenticator.id) } "[stateless] discard the cookie from response" in new WithApplication with Context { @@ -515,14 +517,14 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang c.domain must be equalTo settings.cookieDomain c.secure must be equalTo settings.secureCookie } - there was no(repository).remove(authenticator.id) + verify(repository, never()).remove(authenticator.id) } "throws an AuthenticatorDiscardingException exception if an error occurred during discarding" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val okResult = Results.Ok - repository.remove(any()) returns Future.failed(new Exception("Cannot store authenticator")) + when(repository.remove(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator"))) await(service(Some(repository)).discard(authenticator, okResult)) must throwA[AuthenticatorDiscardingException].like { case e => @@ -539,27 +541,27 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang /** * The repository implementation. */ - lazy val repository = mock[AuthenticatorRepository[CookieAuthenticator]].smart + lazy val repository = mockSmart[AuthenticatorRepository[CookieAuthenticator]] /** * The ID generator implementation. */ - lazy val fingerprintGenerator = mock[FingerprintGenerator].smart + lazy val fingerprintGenerator = mockSmart[FingerprintGenerator] /** * The ID generator implementation. */ - lazy val idGenerator = mock[IDGenerator].smart + lazy val idGenerator = mockSmart[IDGenerator] /** * The signer implementation. * - * The signer returns the same value as passed to the methods. This is enough for testing. + * The signer).thenReturn(the same value as passed to the methods. This is enough for testing.) */ lazy val signer = { - val c = mock[Signer].smart - c.sign(any()) answers { p: Any => p.asInstanceOf[String] } - c.extract(any()) answers { p: Any => Success(p.asInstanceOf[String]) } + val c = mockSmart[Signer] + when(c.sign(any())).thenAnswer { _.getArgument(0).asInstanceOf[String] } + when(c.extract(any())).thenAnswer { p => Success(p.getArgument(0).asInstanceOf[String]) } c } @@ -574,7 +576,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang /** * The clock implementation. */ - lazy val clock = mock[Clock].smart + lazy val clock = mockSmart[Clock] /** * The settings. diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/DummyAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/DummyAuthenticatorSpec.scala index a28d7978..574fd9e5 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/DummyAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/DummyAuthenticatorSpec.scala @@ -17,7 +17,6 @@ package io.github.honeycombcheesecake.play.silhouette.impl.authenticators import io.github.honeycombcheesecake.play.silhouette.api.LoginInfo import io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorResult -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.mvc.{ AnyContentAsEmpty, Results } import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } @@ -27,7 +26,7 @@ import scala.concurrent.ExecutionContext.Implicits.global /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.DummyAuthenticator]]. */ -class DummyAuthenticatorSpec extends PlaySpecification with Mockito { +class DummyAuthenticatorSpec extends PlaySpecification { "The `isValid` method of the authenticator" should { "return true" in new Context { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala index 33fabf57..caffac07 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala @@ -27,11 +27,13 @@ import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.JWTAuth import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.JWTAuthenticatorService._ import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.JsonMatchers -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.libs.json.{ JsNull, JsObject, Json } import play.api.mvc.{ AnyContentAsEmpty, Results } import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any +import test.Helper.mockSmart import java.time.temporal.ChronoField import java.time.{ ZoneId, ZonedDateTime } @@ -43,7 +45,7 @@ import scala.language.postfixOps /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.JWTAuthenticator]]. */ -class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatchers with NoLanguageFeatures { +class JWTAuthenticatorSpec extends PlaySpecification with JsonMatchers with NoLanguageFeatures { "The `isValid` method of the authenticator" should { "return false if the authenticator is expired" in new Context { @@ -163,7 +165,7 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch val jwt: String = serialize(authenticatorCustomClock, authenticatorEncoder, settings) - clock.now returns lastUsedDateTime + when(clock.now).thenReturn(lastUsedDateTime) implicit val customClock: Option[Clock] = Some(clock) unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticatorCustomClock.copy( @@ -194,8 +196,8 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val id = "test-id" - idGenerator.generate returns Future.successful(id) - clock.now returns ZonedDateTime.now + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(ZonedDateTime.now) await(service(None).create(loginInfo)).id must be equalTo id } @@ -204,8 +206,8 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val now = ZonedDateTime.now - idGenerator.generate returns Future.successful("test-id") - clock.now returns now + when(idGenerator.generate).thenReturn(Future.successful("test-id")) + when(clock.now).thenReturn(now) await(service(None).create(loginInfo)).lastUsedDateTime must be equalTo now } @@ -214,8 +216,8 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val now = ZonedDateTime.now - idGenerator.generate returns Future.successful("test-id") - clock.now returns now + when(idGenerator.generate).thenReturn(Future.successful("test-id")) + when(clock.now).thenReturn(now) await(service(None).create(loginInfo)).expirationDateTime must be equalTo now + 12.hours } @@ -225,9 +227,9 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch val sixHours = 6 hours val now = ZonedDateTime.now - settings.authenticatorExpiry returns sixHours - idGenerator.generate returns Future.successful("test-id") - clock.now returns now + when(settings.authenticatorExpiry).thenReturn(sixHours) + when(idGenerator.generate).thenReturn(Future.successful("test-id")) + when(clock.now).thenReturn(now) await(service(None).create(loginInfo)).expirationDateTime must be equalTo now + sixHours } @@ -235,7 +237,7 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch "throws an AuthenticatorCreationException exception if an error occurred during creation" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - idGenerator.generate returns Future.failed(new Exception("Could not generate ID")) + when(idGenerator.generate).thenReturn(Future.failed(new Exception("Could not generate ID"))) await(service(None).create(loginInfo)) must throwA[AuthenticatorCreationException].like { case e => @@ -254,18 +256,18 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch "return None if DAO is enabled and no authenticator is stored for the token located in the header" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> "not-stored") - repository.find(authenticator.id) returns Future.successful(None) + when(repository.find(authenticator.id)).thenReturn(Future.successful(None)) await(service(Some(repository)).retrieve) must beNone } "return authenticator if DAO is enabled and an authenticator is stored for the token located in the the header" in new WithApplication with Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings)) - clock.now returns ZonedDateTime.now + when(clock.now).thenReturn(ZonedDateTime.now) - repository.find(authenticator.id) returns Future.successful(Some(authenticator.copy( + when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator.copy( expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), - lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))) + lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))))) await(service(Some(repository)).retrieve) must beSome(authenticator.copy( expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), @@ -274,12 +276,12 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch "return authenticator if DAO is enabled and an authenticator is stored for the token located in the the query string" in new WithApplication with Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest("GET", s"?${settings.fieldName}=${serialize(authenticator, authenticatorEncoder, settings)}") - clock.now returns ZonedDateTime.now + when(clock.now).thenReturn(ZonedDateTime.now) - settings.requestParts returns Some(Seq(RequestPart.QueryString)) - repository.find(authenticator.id) returns Future.successful(Some(authenticator.copy( + when(settings.requestParts).thenReturn(Some(Seq(RequestPart.QueryString))) + when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator.copy( expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), - lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0)))) + lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))))) await(service(Some(repository)).retrieve) must beSome(authenticator.copy( expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), @@ -288,30 +290,30 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch "return authenticator if DAO is disabled and authenticator was found in the header" in new WithApplication with Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings)) - clock.now returns ZonedDateTime.now + when(clock.now).thenReturn(ZonedDateTime.now) await(service(None).retrieve) must beSome(authenticator.copy( expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) - there was no(repository).find(any()) + verify(repository, never()).find(any()) } "return authenticator if DAO is disabled and authenticator was found in the query string" in new WithApplication with Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest("GET", s"?${settings.fieldName}=${serialize(authenticator, authenticatorEncoder, settings)}") - clock.now returns ZonedDateTime.now + when(clock.now).thenReturn(ZonedDateTime.now) - settings.requestParts returns Some(Seq(RequestPart.QueryString)) + when(settings.requestParts).thenReturn(Some(Seq(RequestPart.QueryString))) await(service(None).retrieve) must beSome(authenticator.copy( expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) - there was no(repository).find(any()) + verify(repository, never()).find(any()) } "throws an AuthenticatorRetrievalException exception if an error occurred during retrieval" in new WithApplication with Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings)) - clock.now returns ZonedDateTime.now + when(clock.now).thenReturn(ZonedDateTime.now) - repository.find(authenticator.id) returns Future.failed(new RuntimeException("Cannot find authenticator")) + when(repository.find(authenticator.id)).thenReturn(Future.failed(new RuntimeException("Cannot find authenticator"))) await(service(Some(repository)).retrieve) must throwA[AuthenticatorRetrievalException].like { case e => @@ -322,13 +324,13 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch "The `init` method of the service" should { "return the token if DAO is enabled and authenticator could be saved in backing store" in new WithApplication with Context { - repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[JWTAuthenticator]) } + when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val token = await(service(Some(repository)).init(authenticator)) unserialize(token, authenticatorEncoder, settings).get must be equalTo authenticator - there was one(repository).add(any()) + verify(repository).add(any()) } "return the token if DAO is disabled" in new WithApplication with Context { @@ -337,11 +339,11 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch val token = await(service(None).init(authenticator)) unserialize(token, authenticatorEncoder, settings).get must be equalTo authenticator - there was no(repository).add(any()) + verify(repository, never()).add(any()) } "throws an AuthenticatorInitializationException exception if an error occurred during initialization" in new Context { - repository.add(any()) returns Future.failed(new Exception("Cannot store authenticator")) + when(repository.add(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator"))) implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val okResult = Future.successful(Results.Ok) @@ -398,8 +400,8 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch "The `touch` method of the service" should { "update the last used date if idle timeout is defined" in new WithApplication with Context { - settings.authenticatorIdleTimeout returns Some(1 second) - clock.now returns ZonedDateTime.now + when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second)) + when(clock.now).thenReturn(ZonedDateTime.now) service(None).touch(authenticator) must beLeft[JWTAuthenticator].like { case a => @@ -408,8 +410,8 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch } "do not update the last used date if idle timeout is not defined" in new WithApplication with Context { - settings.authenticatorIdleTimeout returns None - clock.now returns ZonedDateTime.now + when(settings.authenticatorIdleTimeout).thenReturn(None) + when(clock.now).thenReturn(ZonedDateTime.now) service(None).touch(authenticator) must beRight[JWTAuthenticator].like { case a => @@ -420,39 +422,39 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch "The `update` method of the service" should { "update the authenticator in backing store" in new WithApplication with Context { - repository.update(any()) answers { _: Any => Future.successful(authenticator) } + when(repository.update(any())).thenAnswer { _ => Future.successful(authenticator) } implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() await(service(Some(repository)).update(authenticator, Results.Ok)) - there was one(repository).update(authenticator) + verify(repository).update(authenticator) } "return the result if the authenticator could be stored in backing store" in new WithApplication with Context { - repository.update(any()) answers { p: Any => Future.successful(p.asInstanceOf[JWTAuthenticator]) } + when(repository.update(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val result = service(Some(repository)).update(authenticator, Results.Ok) status(result) must be equalTo OK unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator - there was one(repository).update(authenticator) + verify(repository).update(authenticator) } "return the result if backing store is disabled" in new WithApplication with Context { - repository.update(any()) answers { p: Any => Future.successful(p.asInstanceOf[JWTAuthenticator]) } + when(repository.update(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val result = service(None).update(authenticator, Results.Ok) status(result) must be equalTo OK unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator - there was no(repository).update(any()) + verify(repository, never()).update(any()) } "throws an AuthenticatorUpdateException exception if an error occurred during update" in new WithApplication with Context { - repository.update(any()) returns Future.failed(new Exception("Cannot store authenticator")) + when(repository.update(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator"))) implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() @@ -468,10 +470,10 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val id = "new-test-id" - repository.remove(any()) answers { _: Any => Future.successful(()) } - repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[JWTAuthenticator]) } - idGenerator.generate returns Future.successful(id) - clock.now returns ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0) + when(repository.remove(any())).thenAnswer { _ => Future.successful(()) } + when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)) val result = service(Some(repository)).renew(authenticator, Results.Ok) @@ -480,18 +482,18 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch expirationDateTime = clock.now + settings.authenticatorExpiry, lastUsedDateTime = clock.now) - there was one(repository).add(any()) - there was one(repository).remove(authenticator.id) + verify(repository).add(any()) + verify(repository).remove(authenticator.id) } "renew an authenticator with custom claims" in new WithApplication with Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val id = "new-test-id" - repository.remove(any()) returns Future.successful(()) - repository.add(any()) answers { p: Any => Future.successful(p.asInstanceOf[JWTAuthenticator]) } - idGenerator.generate returns Future.successful(id) - clock.now returns ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0) + when(repository.remove(any())).thenReturn(Future.successful(())) + when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)) val result = service(Some(repository)).renew(authenticator.copy(customClaims = Some(customClaims)), Results.Ok) @@ -501,16 +503,16 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch lastUsedDateTime = clock.now, customClaims = Some(customClaims)) - there was one(repository).add(any()) - there was one(repository).remove(authenticator.id) + verify(repository).add(any()) + verify(repository).remove(authenticator.id) } "renew the authenticator and return the response with a new JWT if DAO is disabled" in new WithApplication with Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val id = "new-test-id" - idGenerator.generate returns Future.successful(id) - clock.now returns ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0) + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)) val result = service(None).renew(authenticator, Results.Ok) @@ -518,8 +520,8 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch id = id, expirationDateTime = clock.now + settings.authenticatorExpiry, lastUsedDateTime = clock.now) - there was no(repository).remove(any()) - there was no(repository).add(any()) + verify(repository, never()).remove(any()) + verify(repository, never()).add(any()) } "throws an AuthenticatorRenewalException exception if an error occurred during renewal" in new Context { @@ -527,10 +529,10 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch val now = ZonedDateTime.now val id = "new-test-id" - repository.remove(any()) returns Future.successful(()) - repository.add(any()) returns Future.failed(new Exception("Cannot store authenticator")) - idGenerator.generate returns Future.successful(id) - clock.now returns now + when(repository.remove(any())).thenReturn(Future.successful(())) + when(repository.add(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator"))) + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(now) await(service(Some(repository)).renew(authenticator, Results.Ok)) must throwA[AuthenticatorRenewalException].like { case e => @@ -543,11 +545,11 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch "remove authenticator from backing store if DAO is enabled" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - repository.remove(authenticator.id) returns Future.successful(authenticator) + when(repository.remove(authenticator.id)).thenReturn(Future.successful(authenticator)) await(service(Some(repository)).discard(authenticator, Results.Ok)) - there was one(repository).remove(authenticator.id) + verify(repository).remove(authenticator.id) } "do not remove the authenticator from backing store if DAO is disabled" in new Context { @@ -555,14 +557,14 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch await(service(None).discard(authenticator, Results.Ok)) - there was no(repository).remove(authenticator.id) + verify(repository, never()).remove(authenticator.id) } "throws an AuthenticatorDiscardingException exception if an error occurred during discarding" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val okResult = Results.Ok - repository.remove(authenticator.id) returns Future.failed(new Exception("Cannot remove authenticator")) + when(repository.remove(authenticator.id)).thenReturn(Future.failed(new Exception("Cannot remove authenticator"))) await(service(Some(repository)).discard(authenticator, okResult)) must throwA[AuthenticatorDiscardingException].like { case e => @@ -581,7 +583,7 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch /** * The repository implementation. */ - lazy val repository = mock[AuthenticatorRepository[JWTAuthenticator]].smart + lazy val repository = mockSmart[AuthenticatorRepository[JWTAuthenticator]] /** * The authenticator encoder implementation. @@ -591,12 +593,12 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch /** * The ID generator implementation. */ - lazy val idGenerator = mock[IDGenerator].smart + lazy val idGenerator = mockSmart[IDGenerator] /** * The clock implementation. */ - lazy val clock: Clock = mock[Clock].smart + lazy val clock: Clock = mockSmart[Clock] /** * The settings. diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala index 97aae2c0..8326de3e 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala @@ -26,11 +26,13 @@ import io.github.honeycombcheesecake.play.silhouette.api.{ Authenticator, LoginI import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.SessionAuthenticator._ import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.SessionAuthenticatorService._ import org.specs2.control.NoLanguageFeatures -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.libs.json.Json import play.api.mvc._ import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any +import test.Helper.{ mockSmart, mock } import java.time.ZonedDateTime import scala.concurrent.ExecutionContext.Implicits.global @@ -41,7 +43,7 @@ import scala.language.postfixOps /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.SessionAuthenticator]]. */ -class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLanguageFeatures { +class SessionAuthenticatorSpec extends PlaySpecification with NoLanguageFeatures { "The `isValid` method of the authenticator" should { "return false if the authenticator is expired" in new Context { @@ -88,9 +90,9 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan "return a fingerprinted authenticator" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - clock.now returns ZonedDateTime.now - fingerprintGenerator.generate(any) returns "test" - settings.useFingerprinting returns true + when(clock.now).thenReturn(ZonedDateTime.now) + when(fingerprintGenerator.generate(any)).thenReturn("test") + when(settings.useFingerprinting).thenReturn(true) await(service.create(loginInfo)).fingerprint must beSome("test") } @@ -98,8 +100,8 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan "return a non fingerprinted authenticator" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - clock.now returns ZonedDateTime.now - settings.useFingerprinting returns false + when(clock.now).thenReturn(ZonedDateTime.now) + when(settings.useFingerprinting).thenReturn(false) await(service.create(loginInfo)).fingerprint must beNone } @@ -108,7 +110,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val now = ZonedDateTime.now - clock.now returns now + when(clock.now).thenReturn(now) await(service.create(loginInfo)).lastUsedDateTime must be equalTo now } @@ -117,7 +119,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val now = ZonedDateTime.now - clock.now returns now + when(clock.now).thenReturn(now) await(service.create(loginInfo)).expirationDateTime must be equalTo now + 12.hours } @@ -127,8 +129,8 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan val sixHours = 6 hours val now = ZonedDateTime.now - clock.now returns now - settings.authenticatorExpiry returns sixHours + when(clock.now).thenReturn(now) + when(settings.authenticatorExpiry).thenReturn(sixHours) await(service.create(loginInfo)).expirationDateTime must be equalTo now + sixHours } @@ -136,7 +138,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan "throws an AuthenticatorCreationException exception if an error occurred during creation" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - clock.now throws new RuntimeException("Could not get date") + when(clock.now).thenThrow(new RuntimeException("Could not get date")) await(service.create(loginInfo)) must throwA[AuthenticatorCreationException].like { case e => @@ -155,7 +157,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan "return None if session contains invalid json" in new WithApplication with Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode("{")) - settings.useFingerprinting returns false + when(settings.useFingerprinting).thenReturn(false) await(service.retrieve) must beNone } @@ -163,15 +165,15 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan "return None if session contains valid json but invalid authenticator" in new WithApplication with Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode("{ \"test\": \"test\" }")) - settings.useFingerprinting returns false + when(settings.useFingerprinting).thenReturn(false) await(service.retrieve) must beNone } "return None if authenticator fingerprint doesn't match current fingerprint" in new WithApplication with Context { - fingerprintGenerator.generate(any) returns "false" - settings.useFingerprinting returns true - authenticator.fingerprint returns Some("test") + when(fingerprintGenerator.generate(any)).thenReturn("false") + when(settings.useFingerprinting).thenReturn(true) + when(authenticator.fingerprint).thenReturn(Some("test")) implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) @@ -179,9 +181,9 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan } "return authenticator if authenticator fingerprint matches current fingerprint" in new WithApplication with Context { - fingerprintGenerator.generate(any) returns "test" - settings.useFingerprinting returns true - authenticator.fingerprint returns Some("test") + when(fingerprintGenerator.generate(any)).thenReturn("test") + when(settings.useFingerprinting).thenReturn(true) + when(authenticator.fingerprint).thenReturn(Some("test")) implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) @@ -191,7 +193,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan "return authenticator if fingerprinting is disabled" in new WithApplication with Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) - settings.useFingerprinting returns false + when(settings.useFingerprinting).thenReturn(false) await(service.retrieve) must beSome(authenticator) } @@ -200,7 +202,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() .withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) - settings.useFingerprinting returns false + when(settings.useFingerprinting).thenReturn(false) await(service.retrieve) must beSome(authenticator) } @@ -208,8 +210,8 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan "throws an AuthenticatorRetrievalException exception if an error occurred during retrieval" in new WithApplication with Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) - fingerprintGenerator.generate(any) throws new RuntimeException("Could not generate fingerprint") - settings.useFingerprinting returns true + when(fingerprintGenerator.generate(any)).thenThrow(new RuntimeException("Could not generate fingerprint")) + when(settings.useFingerprinting).thenReturn(true) await(service.retrieve) must throwA[AuthenticatorRetrievalException].like { case e => @@ -312,8 +314,8 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan "The `touch` method of the service" should { "update the last used date if idle timeout is defined" in new WithApplication with Context { - settings.authenticatorIdleTimeout returns Some(1 second) - clock.now returns ZonedDateTime.now + when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second)) + when(clock.now).thenReturn(ZonedDateTime.now) service.touch(authenticator) must beLeft[SessionAuthenticator].like { case a => @@ -322,8 +324,8 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan } "do not update the last used date if idle timeout is not defined" in new WithApplication with Context { - settings.authenticatorIdleTimeout returns None - clock.now returns ZonedDateTime.now + when(settings.authenticatorIdleTimeout).thenReturn(None) + when(clock.now).thenReturn(ZonedDateTime.now) service.touch(authenticator) must beRight[SessionAuthenticator].like { case a => @@ -362,7 +364,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan "throws an AuthenticatorUpdateException exception if an error occurred during update" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = spy(FakeRequest()) - request.session throws new RuntimeException("Cannot get session") + when(request.session).thenThrow(new RuntimeException("Cannot get session")) await(service.update(authenticator, Results.Ok)) must throwA[AuthenticatorUpdateException].like { case e => @@ -379,8 +381,8 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan lastUsedDateTime = now, expirationDateTime = now + settings.authenticatorExpiry)).toString()) - settings.useFingerprinting returns false - clock.now returns now + when(settings.useFingerprinting).thenReturn(false) + when(clock.now).thenReturn(now) val result = service.renew(authenticator, Results.Ok) @@ -391,8 +393,8 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing") val now = ZonedDateTime.now - settings.useFingerprinting returns false - clock.now returns now + when(settings.useFingerprinting).thenReturn(false) + when(clock.now).thenReturn(now) val result = service.renew(authenticator, Results.Ok) @@ -405,8 +407,8 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep") val now = ZonedDateTime.now - settings.useFingerprinting returns false - clock.now returns now + when(settings.useFingerprinting).thenReturn(false) + when(clock.now).thenReturn(now) val result = service.renew(authenticator, Results.Ok.addingToSession( "result-other" -> "keep")) @@ -423,9 +425,9 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan val now = ZonedDateTime.now val okResult = (_: Authenticator) => Future.successful(Results.Ok) - request.session throws new RuntimeException("Cannot get session") - settings.useFingerprinting returns false - clock.now returns now + when(request.session).thenThrow(new RuntimeException("Cannot get session")) + when(settings.useFingerprinting).thenReturn(false) + when(clock.now).thenReturn(now) await(service.renew(authenticator, Results.Ok)) must throwA[AuthenticatorRenewalException].like { case e => @@ -457,7 +459,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan implicit val request: FakeRequest[AnyContentAsEmpty.type] = spy(FakeRequest()).withSession(settings.sessionKey -> "test") val result = mock[Result] - result.removingFromSession(any)(any) throws new RuntimeException("Cannot get session") + when(result.removingFromSession(any)(any)).thenThrow(new RuntimeException("Cannot get session")) await(service.discard(authenticator, result)) must throwA[AuthenticatorDiscardingException].like { case e => @@ -474,7 +476,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan /** * The ID generator implementation. */ - lazy val fingerprintGenerator = mock[FingerprintGenerator].smart + lazy val fingerprintGenerator = mockSmart[FingerprintGenerator] /** * The authenticator encoder implementation. @@ -484,7 +486,7 @@ class SessionAuthenticatorSpec extends PlaySpecification with Mockito with NoLan /** * The clock implementation. */ - lazy val clock = mock[Clock].smart + lazy val clock = mockSmart[Clock] /** * The settings. diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/BasicAuthProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/BasicAuthProviderSpec.scala index 48f94168..7ada25fb 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/BasicAuthProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/BasicAuthProviderSpec.scala @@ -21,6 +21,7 @@ import io.github.honeycombcheesecake.play.silhouette.api.exceptions._ import io.github.honeycombcheesecake.play.silhouette.api.util._ import io.github.honeycombcheesecake.play.silhouette.impl.providers.PasswordProvider._ import play.api.test.{ FakeRequest, WithApplication } +import org.mockito.Mockito._ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -36,7 +37,7 @@ class BasicAuthProviderSpec extends PasswordProviderSpec { val loginInfo = LoginInfo(provider.id, credentials.identifier) val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo)) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) await(provider.authenticate(request)) must throwA[ConfigurationException].like { case e => e.getMessage must beEqualTo(HasherIsNotRegistered.format(provider.id, "unknown", "foo, bar")) @@ -47,7 +48,7 @@ class BasicAuthProviderSpec extends PasswordProviderSpec { val loginInfo = new LoginInfo(provider.id, credentials.identifier) val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(None) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(None)) await(provider.authenticate(request)) must beNone } @@ -57,8 +58,8 @@ class BasicAuthProviderSpec extends PasswordProviderSpec { val loginInfo = LoginInfo(provider.id, credentials.identifier) val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) - fooHasher.matches(passwordInfo, credentials.password) returns false - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo)) + when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(false) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) await(provider.authenticate(request)) must beNone } @@ -78,8 +79,8 @@ class BasicAuthProviderSpec extends PasswordProviderSpec { val loginInfo = LoginInfo(provider.id, credentials.identifier) val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) - fooHasher.matches(passwordInfo, credentials.password) returns true - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo)) + when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) await(provider.authenticate(request)) must beSome(loginInfo) } @@ -90,8 +91,8 @@ class BasicAuthProviderSpec extends PasswordProviderSpec { val loginInfo = LoginInfo(provider.id, credentialsWithColon.identifier) val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentialsWithColon)) - fooHasher.matches(passwordInfo, credentialsWithColon.password) returns true - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo)) + when(fooHasher.matches(passwordInfo, credentialsWithColon.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) await(provider.authenticate(request)) must beSome(loginInfo) } @@ -101,13 +102,13 @@ class BasicAuthProviderSpec extends PasswordProviderSpec { val loginInfo = LoginInfo(provider.id, credentials.identifier) val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) - fooHasher.hash(credentials.password) returns passwordInfo - barHasher.matches(passwordInfo, credentials.password) returns true - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo)) - authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo) returns Future.successful(passwordInfo) + when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo) + when(barHasher.matches(passwordInfo, credentials.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo)) await(provider.authenticate(request)) must beSome(loginInfo) - there was one(authInfoRepository).update(loginInfo, passwordInfo) + verify(authInfoRepository).update(loginInfo, passwordInfo) } "re-hash password with new hasher if password info is deprecated" in new WithApplication with Context { @@ -115,14 +116,14 @@ class BasicAuthProviderSpec extends PasswordProviderSpec { val loginInfo = LoginInfo(provider.id, credentials.identifier) val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) - fooHasher.isDeprecated(passwordInfo) returns Some(true) - fooHasher.hash(credentials.password) returns passwordInfo - fooHasher.matches(passwordInfo, credentials.password) returns true - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo)) - authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo) returns Future.successful(passwordInfo) + when(fooHasher.isDeprecated(passwordInfo)).thenReturn(Some(true)) + when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo) + when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo)) await(provider.authenticate(request)) must beSome(loginInfo) - there was one(authInfoRepository).update(loginInfo, passwordInfo) + verify(authInfoRepository).update(loginInfo, passwordInfo) } "return None if Authorization method is not Basic and Base64 decoded header has ':'" in new WithApplication with Context { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/CredentialsProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/CredentialsProviderSpec.scala index 93ca5b6b..515f008b 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/CredentialsProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/CredentialsProviderSpec.scala @@ -21,6 +21,7 @@ import io.github.honeycombcheesecake.play.silhouette.api.util.{ Credentials, Pas import io.github.honeycombcheesecake.play.silhouette.impl.exceptions.{ IdentityNotFoundException, InvalidPasswordException } import io.github.honeycombcheesecake.play.silhouette.impl.providers.PasswordProvider._ import play.api.test.WithApplication +import org.mockito.Mockito._ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -34,7 +35,7 @@ class CredentialsProviderSpec extends PasswordProviderSpec { "throw IdentityNotFoundException if no auth info could be found for the given credentials" in new WithApplication with Context { val loginInfo = new LoginInfo(provider.id, credentials.identifier) - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(None) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(None)) await(provider.authenticate(credentials)) must throwA[IdentityNotFoundException].like { case e => e.getMessage must beEqualTo(PasswordInfoNotFound.format(provider.id, loginInfo)) @@ -45,8 +46,8 @@ class CredentialsProviderSpec extends PasswordProviderSpec { val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") val loginInfo = LoginInfo(provider.id, credentials.identifier) - fooHasher.matches(passwordInfo, credentials.password) returns false - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo)) + when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(false) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) await(provider.authenticate(credentials)) must throwA[InvalidPasswordException].like { case e => e.getMessage must beEqualTo(PasswordDoesNotMatch.format(provider.id)) @@ -57,7 +58,7 @@ class CredentialsProviderSpec extends PasswordProviderSpec { val passwordInfo = PasswordInfo("unknown", "hashed(s3cr3t)") val loginInfo = LoginInfo(provider.id, credentials.identifier) - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo)) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) await(provider.authenticate(credentials)) must throwA[ConfigurationException].like { case e => e.getMessage must beEqualTo(HasherIsNotRegistered.format(provider.id, "unknown", "foo, bar")) @@ -68,8 +69,8 @@ class CredentialsProviderSpec extends PasswordProviderSpec { val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") val loginInfo = LoginInfo(provider.id, credentials.identifier) - fooHasher.matches(passwordInfo, credentials.password) returns true - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo)) + when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) await(provider.authenticate(credentials)) must be equalTo loginInfo } @@ -78,27 +79,27 @@ class CredentialsProviderSpec extends PasswordProviderSpec { val passwordInfo = PasswordInfo("bar", "hashed(s3cr3t)") val loginInfo = LoginInfo(provider.id, credentials.identifier) - fooHasher.hash(credentials.password) returns passwordInfo - barHasher.matches(passwordInfo, credentials.password) returns true - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo)) - authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo) returns Future.successful(passwordInfo) + when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo) + when(barHasher.matches(passwordInfo, credentials.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo)) await(provider.authenticate(credentials)) must be equalTo loginInfo - there was one(authInfoRepository).update(loginInfo, passwordInfo) + verify(authInfoRepository).update(loginInfo, passwordInfo) } "re-hash password with new hasher if hasher is deprecated" in new WithApplication with Context { val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") val loginInfo = LoginInfo(provider.id, credentials.identifier) - fooHasher.isDeprecated(passwordInfo) returns Some(true) - fooHasher.hash(credentials.password) returns passwordInfo - fooHasher.matches(passwordInfo, credentials.password) returns true - authInfoRepository.find[PasswordInfo](loginInfo) returns Future.successful(Some(passwordInfo)) - authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo) returns Future.successful(passwordInfo) + when(fooHasher.isDeprecated(passwordInfo)).thenReturn(Some(true)) + when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo) + when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo)) await(provider.authenticate(credentials)) must be equalTo loginInfo - there was one(authInfoRepository).update(loginInfo, passwordInfo) + verify(authInfoRepository).update(loginInfo, passwordInfo) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/DefaultSocialStateHandlerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/DefaultSocialStateHandlerSpec.scala index b6e28e70..053e2611 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/DefaultSocialStateHandlerSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/DefaultSocialStateHandlerSpec.scala @@ -21,11 +21,13 @@ import io.github.honeycombcheesecake.play.silhouette.api.util.ExtractableRequest import io.github.honeycombcheesecake.play.silhouette.impl.providers.DefaultSocialStateHandler._ import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialStateItem.ItemStructure import org.specs2.matcher.JsonMatchers -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.libs.json.Json import play.api.mvc.{ AnyContentAsEmpty, Results } import play.api.test.{ FakeRequest, PlaySpecification } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any +import test.Helper.mockSmart import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -34,11 +36,11 @@ import scala.util.{ Failure, Success } /** * Test case for the [[DefaultSocialStateHandler]] class. */ -class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with JsonMatchers { +class DefaultSocialStateHandlerSpec extends PlaySpecification with JsonMatchers { "The `withHandler` method" should { "return a new state handler with the given item handler added" in new Context { - val newHandler = mock[SocialStateItemHandler].smart + val newHandler = mockSmart[SocialStateItemHandler] stateHandler.handlers.size must be equalTo 2 stateHandler.withHandler(newHandler).handlers.size must be equalTo 3 @@ -47,8 +49,8 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with "The `state` method" should { "return the social state" in new Context { - Default.itemHandler.item returns Future.successful(Default.item) - Publishable.itemHandler.item returns Future.successful(Publishable.item) + when(Default.itemHandler.item).thenReturn(Future.successful(Default.item)) + when(Publishable.itemHandler.item).thenReturn(Future.successful(Publishable.item)) await(stateHandler.state) must be equalTo state } @@ -66,13 +68,13 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with } "return the serialized social state" in new Context { - Default.itemHandler.canHandle(Publishable.item) returns None - Default.itemHandler.canHandle(Default.item) returns Some(Default.item) - Default.itemHandler.serialize(Default.item) returns Default.structure + when(Default.itemHandler.canHandle(Publishable.item)).thenReturn(None) + when(Default.itemHandler.canHandle(Default.item)).thenReturn(Some(Default.item)) + when(Default.itemHandler.serialize(Default.item)).thenReturn(Default.structure) - Publishable.itemHandler.canHandle(Default.item) returns None - Publishable.itemHandler.canHandle(Publishable.item) returns Some(Publishable.item) - Publishable.itemHandler.serialize(Publishable.item) returns Publishable.structure + when(Publishable.itemHandler.canHandle(Default.item)).thenReturn(None) + when(Publishable.itemHandler.canHandle(Publishable.item)).thenReturn(Some(Publishable.item)) + when(Publishable.itemHandler.serialize(Publishable.item)).thenReturn(Publishable.structure) stateHandler.serialize(state) must be equalTo s"${Publishable.structure.asString}.${Default.structure.asString}" } @@ -85,7 +87,7 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with await(stateHandler.unserialize("")) - there was no(signer).extract(any[String]()) + verify(signer, never()).extract(any[String]()) } "throw an Exception for an empty string" in new Context { @@ -111,8 +113,8 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with implicit val request: ExtractableRequest[AnyContentAsEmpty.type] = new ExtractableRequest(FakeRequest()) val serialized = s"${Default.structure.asString}" - Default.itemHandler.canHandle(any[ItemStructure]())(any()) returns false - Publishable.itemHandler.canHandle(any[ItemStructure]())(any()) returns false + when(Default.itemHandler.canHandle(any[ItemStructure]())(any())).thenReturn(false) + when(Publishable.itemHandler.canHandle(any[ItemStructure]())(any())).thenReturn(false) await(stateHandler.unserialize(serialized)) must throwA[ProviderException].like { case e => @@ -124,13 +126,13 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with implicit val request: ExtractableRequest[AnyContentAsEmpty.type] = new ExtractableRequest(FakeRequest()) val serialized = s"${Default.structure.asString}.${Publishable.structure.asString}" - Default.itemHandler.canHandle(Publishable.structure) returns false - Default.itemHandler.canHandle(Default.structure) returns true - Default.itemHandler.unserialize(Default.structure) returns Future.successful(Default.item) + when(Default.itemHandler.canHandle(Publishable.structure)).thenReturn(false) + when(Default.itemHandler.canHandle(Default.structure)).thenReturn(true) + when(Default.itemHandler.unserialize(Default.structure)).thenReturn(Future.successful(Default.item)) - Publishable.itemHandler.canHandle(Default.structure) returns false - Publishable.itemHandler.canHandle(Publishable.structure) returns true - Publishable.itemHandler.unserialize(Publishable.structure) returns Future.successful(Publishable.item) + when(Publishable.itemHandler.canHandle(Default.structure)).thenReturn(false) + when(Publishable.itemHandler.canHandle(Publishable.structure)).thenReturn(true) + when(Publishable.itemHandler.unserialize(Publishable.structure)).thenReturn(Future.successful(Publishable.item)) await(stateHandler.unserialize(serialized)) must be equalTo SocialState(Set(Default.item, Publishable.item)) } @@ -142,9 +144,9 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with val result = Results.Ok val publishedResult = Results.Ok.withHeaders("X-PUBLISHED" -> "true") - Publishable.itemHandler.publish(Publishable.item, result) returns publishedResult - Publishable.itemHandler.canHandle(Default.item) returns None - Publishable.itemHandler.canHandle(Publishable.item) returns Some(Publishable.item) + when(Publishable.itemHandler.publish(Publishable.item, result)).thenReturn(publishedResult) + when(Publishable.itemHandler.canHandle(Default.item)).thenReturn(None) + when(Publishable.itemHandler.canHandle(Publishable.item)).thenReturn(Some(Publishable.item)) stateHandler.publish(result, state) must be equalTo publishedResult } @@ -153,8 +155,8 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val result = Results.Ok - Publishable.itemHandler.canHandle(Default.item) returns None - Publishable.itemHandler.canHandle(Publishable.item) returns None + when(Publishable.itemHandler.canHandle(Default.item)).thenReturn(None) + when(Publishable.itemHandler.canHandle(Publishable.item)).thenReturn(None) stateHandler.publish(result, state) must be equalTo result } @@ -173,7 +175,7 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with type Item = DefaultItem } object Default { - val itemHandler = mock[DefaultItemHandler].smart + val itemHandler = mockSmart[DefaultItemHandler] val item = DefaultItem() val structure = ItemStructure("default", Json.obj()) } @@ -186,7 +188,7 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with type Item = PublishableItem } object Publishable { - val itemHandler = mock[PublishableItemHandler].smart + val itemHandler = mockSmart[PublishableItemHandler] val item = PublishableItem() val structure = ItemStructure("publishable", Json.obj()) } @@ -197,10 +199,10 @@ class DefaultSocialStateHandlerSpec extends PlaySpecification with Mockito with * The signer returns the same value as passed to the methods. This is enough for testing. */ val signer = { - val c = mock[Signer].smart - c.sign(any()) answers { p: Any => p.asInstanceOf[String] } - c.extract(any()) answers { p: Any => - p.asInstanceOf[String] match { + val c = mockSmart[Signer] + when(c.sign(any())).thenAnswer(_.getArgument(0).asInstanceOf[String]) + when(c.extract(any())).thenAnswer { p => + p.getArgument(0).asInstanceOf[String] match { case "" => Failure(new RuntimeException("Wrong state format")) case s => Success(s) } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala index 8450b3b4..54ebf2e7 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala @@ -20,12 +20,16 @@ import io.github.honeycombcheesecake.play.silhouette.impl.exceptions.{ AccessDen import io.github.honeycombcheesecake.play.silhouette.impl.providers.OAuth1Provider._ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.services.PlayOAuth1Service import org.specs2.matcher.ThrownExpectations -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.mvc.{ AnyContent, AnyContentAsEmpty, Result, Results } import play.api.test.{ FakeHeaders, FakeRequest, WithApplication } import play.mvc.Http.HeaderNames +import org.hamcrest.core.IsAnything +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ +import org.mockito.hamcrest.MockitoHamcrest import test.SocialProviderSpec +import test.Helper.mock import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -42,7 +46,7 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] { val c = context "throw a RuntimeException if the unsafe 1.0 specification should be used" in new WithApplication { implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Denied + "=") - c.oAuthService.use10a returns false + when(c.oAuthService.use10a).thenReturn(false) c.provider.authenticate() must throwA[RuntimeException] } } @@ -58,7 +62,7 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] { "fail with an UnexpectedResponseException if request token cannot be retrieved" in new WithApplication { implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - c.oAuthService.retrieveRequestToken(c.oAuthSettings.callbackURL) returns Future.failed(new Exception("")) + when(c.oAuthService.retrieveRequestToken(c.oAuthSettings.callbackURL)).thenReturn(Future.failed(new Exception(""))) failed[UnexpectedResponseException](c.provider.authenticate()) { case e => e.getMessage must startWith(ErrorRequestToken.format(c.provider.id, "")) @@ -69,12 +73,11 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] { implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val serializedTokenSecret = "my.serialized.token.secret" - c.oAuthService.retrieveRequestToken(c.oAuthSettings.callbackURL) returns Future.successful(c.oAuthInfo) - c.oAuthService.redirectUrl(any()) answers { _: Any => c.oAuthSettings.authorizationURL } - c.oAuthTokenSecretProvider.build(any())(any(), any()) returns Future.successful(c.oAuthTokenSecret) - c.oAuthTokenSecretProvider.publish(any(), any())(any()) answers { (a, _) => - a.asInstanceOf[Array[Any]](0).asInstanceOf[Result] - } + when(c.oAuthService.retrieveRequestToken(c.oAuthSettings.callbackURL)).thenReturn(Future.successful(c.oAuthInfo)) + when(c.oAuthService.redirectUrl(any())).thenAnswer(_ => c.oAuthSettings.authorizationURL) + when(c.oAuthTokenSecretProvider.build(any())(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret)) + // when(c.oAuthTokenSecretProvider.publish(any(), any())(any())).thenAnswer(_.getArguments.asInstanceOf[Array[Any]](0).asInstanceOf[Result]) + when(c.oAuthTokenSecretProvider.publish(any(), any())(any())).thenAnswer(_.getArgument(0).asInstanceOf[Result]) result(c.provider.authenticate()) { result => status(result) must equalTo(SEE_OTHER) @@ -90,10 +93,6 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] { verifyCallbackURLResolution("callback-url", secure = false, "http://www.example.com/request-path/callback-url") } - "resolves relative redirectURLs before starting the flow over https" in new WithApplication { - verifyCallbackURLResolution("/callback-url", secure = true, "https://www.example.com/callback-url") - } - def verifyCallbackURLResolution(callbackURL: String, secure: Boolean, resolvedCallbackURL: String) = { implicit val req = FakeRequest[AnyContent]( method = GET, @@ -102,24 +101,30 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] { body = AnyContentAsEmpty, secure = secure) - c.oAuthSettings.callbackURL returns callbackURL + when(c.oAuthSettings.callbackURL).thenReturn(callbackURL) + + when(c.oAuthService.retrieveRequestToken(any())(any())).thenReturn(Future.successful(c.oAuthInfo)) - c.oAuthService.retrieveRequestToken(any())(any()) returns Future.successful(c.oAuthInfo) - c.oAuthService.redirectUrl(c.oAuthInfo.token) returns c.oAuthSettings.authorizationURL - c.oAuthTokenSecretProvider.build(any())(any(), any()) returns Future.successful(c.oAuthTokenSecret) - c.oAuthTokenSecretProvider.publish(any(), any())(any()) answers { _: Any => Results.Redirect(c.oAuthSettings.authorizationURL) } + when(c.oAuthService.redirectUrl(c.oAuthInfo.token)).thenAnswer(_ => c.oAuthSettings.authorizationURL) + + when(c.oAuthTokenSecretProvider.build(any())(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret)) + when(c.oAuthTokenSecretProvider.publish(any(), any())(any())).thenAnswer(_ => Results.Redirect(c.oAuthSettings.authorizationURL)) await(c.provider.authenticate()) - there was one(c.oAuthService).retrieveRequestToken(resolvedCallbackURL) + verify(c.oAuthService).retrieveRequestToken(resolvedCallbackURL) + } + + "resolves relative redirectURLs before starting the flow over https" in new WithApplication { + verifyCallbackURLResolution("/callback-url", secure = true, "https://www.example.com/callback-url") } "fail with an UnexpectedResponseException if access token cannot be retrieved" in new WithApplication { val tokenSecret = "my.token.secret" implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + OAuthVerifier + "=my.verifier&" + OAuthToken + "=my.token") - c.oAuthTokenSecret.value returns tokenSecret - c.oAuthTokenSecretProvider.retrieve(any(), any()) returns Future.successful(c.oAuthTokenSecret) - c.oAuthService.retrieveAccessToken(c.oAuthInfo.copy(secret = tokenSecret), "my.verifier") returns Future.failed(new Exception("")) + when(c.oAuthTokenSecret.value).thenReturn(tokenSecret) + when(c.oAuthTokenSecretProvider.retrieve(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret)) + when(c.oAuthService.retrieveAccessToken(c.oAuthInfo.copy(secret = tokenSecret), "my.verifier")).thenReturn(Future.failed(new Exception(""))) failed[UnexpectedResponseException](c.provider.authenticate()) { case e => e.getMessage must startWith(ErrorAccessToken.format(c.provider.id, "")) @@ -130,9 +135,9 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] { val tokenSecret = "my.token.secret" implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + OAuthVerifier + "=my.verifier&" + OAuthToken + "=my.token") - c.oAuthTokenSecret.value returns tokenSecret - c.oAuthTokenSecretProvider.retrieve(any(), any()) returns Future.successful(c.oAuthTokenSecret) - c.oAuthService.retrieveAccessToken(c.oAuthInfo.copy(secret = tokenSecret), "my.verifier") returns Future.successful(c.oAuthInfo) + when(c.oAuthTokenSecret.value).thenReturn(tokenSecret) + when(c.oAuthTokenSecretProvider.retrieve(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret)) + when(c.oAuthService.retrieveAccessToken(c.oAuthInfo.copy(secret = tokenSecret), "my.verifier")).thenReturn(Future.successful(c.oAuthInfo)) authInfo(c.provider.authenticate())(_ must be equalTo c.oAuthInfo) } @@ -156,7 +161,7 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] { /** * Context for the OAuth1ProviderSpec. */ -trait OAuth1ProviderSpecContext extends Scope with Mockito with ThrownExpectations { +trait OAuth1ProviderSpecContext extends Scope with ThrownExpectations { abstract class TestSecret extends OAuth1TokenSecret abstract class TestStateProvider extends OAuth1TokenSecretProvider { @@ -168,7 +173,7 @@ trait OAuth1ProviderSpecContext extends Scope with Mockito with ThrownExpectatio */ lazy val httpLayer = { val m = mock[MockHTTPLayer] - m.executionContext returns global + when(m.executionContext).thenReturn(global) m } @@ -181,10 +186,11 @@ trait OAuth1ProviderSpecContext extends Scope with Mockito with ThrownExpectatio * The OAuth1 service mock. */ lazy val oAuthService: OAuth1Service = { + val s = mock[PlayOAuth1Service] - s.use10a returns true - s.withSettings(anyFunction1) returns s - s.redirectUrl(anyString) returns "TESTING" + when(s.use10a).thenReturn(true) + when(s.withSettings(MockitoHamcrest.argThat(new IsAnything[OAuth1Settings => OAuth1Settings]))).thenReturn(s) + when(s.redirectUrl(anyString)).thenReturn("TESTING") s } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala index 7d386e25..6ce7fb20 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala @@ -24,13 +24,15 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.OAuth2Provid import io.github.honeycombcheesecake.play.silhouette.impl.providers.state.UserStateItem import io.github.honeycombcheesecake.play.silhouette.helpers.Transform._ import org.specs2.matcher.ThrownExpectations -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.libs.json.{ JsValue, Json } import play.api.mvc.{ AnyContent, AnyContentAsEmpty, Result } import play.api.test.{ FakeHeaders, FakeRequest, WithApplication } import play.mvc.Http.HeaderNames import test.SocialStateProviderSpec +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ +import test.Helper.{ mockSmart, mock } import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.{ ExecutionContext, Future } @@ -65,10 +67,10 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So case Some(_) => implicit val req = FakeRequest(GET, "/") - c.stateProvider.serialize(c.state) returns "session-value" - c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state) - c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state) - c.oAuthSettings.authorizationURL returns None + when(c.stateProvider.serialize(c.state)).thenReturn("session-value") + when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.oAuthSettings.authorizationURL).thenReturn(None) failed[ConfigurationException](c.provider.authenticate()) { case e => e.getMessage must startWith(AuthorizationURLUndefined.format(c.provider.id)) @@ -84,12 +86,12 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So val sessionKey = "session-key" val sessionValue = "session-value" - c.stateProvider.serialize(c.state) returns sessionValue - c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state) - c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state) - c.stateProvider.publish(any, any)(any) answers { (a, _) => - val result = a.asInstanceOf[Array[Any]](0).asInstanceOf[Result] - val state = a.asInstanceOf[Array[Any]](1).asInstanceOf[c.TestState] + when(c.stateProvider.serialize(c.state)).thenReturn(sessionValue) + when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.publish(any, any)(any)).thenAnswer { m => + val result = m.getArgument(0).asInstanceOf[Result] + val state = m.getArgument(1).asInstanceOf[c.TestState] result.withSession(sessionKey -> c.stateProvider.serialize(state)) } @@ -159,13 +161,13 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So val sessionKey = "session-key" val sessionValue = "session-value" - c.oAuthSettings.redirectURL returns Some(redirectURL) + when(c.oAuthSettings.redirectURL).thenReturn(Some(redirectURL)) - c.stateProvider.serialize(c.state) returns sessionValue - c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state) - c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state) - c.stateProvider.publish(any, any)(any) answers { (a, _) => - val result = a.asInstanceOf[Array[Any]](0).asInstanceOf[Result] + when(c.stateProvider.serialize(c.state)).thenReturn(sessionValue) + when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.publish(any, any)(any)).thenAnswer { m => + val result = m.getArgument(0).asInstanceOf[Result] result.withSession(sessionKey -> c.stateProvider.serialize(c.state)) } @@ -192,13 +194,13 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So val sessionKey = "session-key" val sessionValue = "session-value" - c.oAuthSettings.redirectURL returns redirectURL + when(c.oAuthSettings.redirectURL).thenReturn(redirectURL) - c.stateProvider.serialize(c.state) returns sessionValue - c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state) - c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state) - c.stateProvider.publish(any, any)(any) answers { (a, _) => - val result = a.asInstanceOf[Array[Any]](0).asInstanceOf[Result] + when(c.stateProvider.serialize(c.state)).thenReturn(sessionValue) + when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.publish(any, any)(any)).thenAnswer { m => + val result = m.getArgument(0).asInstanceOf[Result] result.withSession(sessionKey -> c.stateProvider.serialize(c.state)) } @@ -226,11 +228,11 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So case Some(_) => implicit val req = FakeRequest(GET, "/") - c.stateProvider.serialize(c.state) returns "" - c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state) - c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state) - c.stateProvider.publish(any, any)(any) answers { (a, _) => - a.asInstanceOf[Array[Any]](0).asInstanceOf[Result] + when(c.stateProvider.serialize(c.state)).thenReturn("") + when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.publish(any, any)(any)).thenAnswer { m => + m.getArgument(0).asInstanceOf[Result] } result(c.provider.authenticate())(result => @@ -251,23 +253,23 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So GrantType -> Seq(AuthorizationCode), Code -> Seq("my.code")) ++ c.oAuthSettings.accessTokenParams.transformValues(Seq(_)) ++ redirectParam.toMap.transformValues(Seq(_)) implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsRequest.withHttpHeaders(any) returns wsRequest + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) // We must use this neat trick here because it isn't possible to check the post call with a verification, // because of the implicit params needed for the post call. On the other hand we can test it in the abstract // spec, because we throw an exception in both cases which stops the test once the post method was called. // This protects as for an NPE because of the not mocked dependencies. The other solution would be to execute // this test in every provider with the full mocked dependencies. - wsRequest.post[Map[String, Seq[String]]](any)(any) answers { (a, _) => - if (a.asInstanceOf[Array[Any]](0).asInstanceOf[Map[String, Seq[String]]].equals(params)) { + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenAnswer { m => + if (m.getArgument(0).asInstanceOf[Map[String, Seq[String]]].equals(params)) { throw new RuntimeException("success") } else { throw new RuntimeException("failure") } } - c.httpLayer.url(c.oAuthSettings.accessTokenURL) returns wsRequest - c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state) - c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state) + when(c.httpLayer.url(c.oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) failed[RuntimeException](c.provider.authenticate()) { case e => e.getMessage must startWith("success") @@ -279,14 +281,14 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json throws new RuntimeException("Unexpected character ('<' (code 60))") - wsResponse.body returns "" - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - c.httpLayer.url(c.oAuthSettings.accessTokenURL) returns wsRequest - c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(c.state) - c.stateProvider.state(any[ExecutionContext]) returns Future.successful(c.state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenThrow(new RuntimeException("Unexpected character ('<' (code 60))")) + when(wsResponse.body).thenReturn("") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(c.httpLayer.url(c.oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) failed[UnexpectedResponseException](c.provider.authenticate()) { case e => e.getMessage must startWith( @@ -313,7 +315,7 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So /** * Context for the OAuth2ProviderSpec. */ -trait OAuth2ProviderSpecContext extends Scope with Mockito with ThrownExpectations { +trait OAuth2ProviderSpecContext extends Scope with ThrownExpectations { abstract class TestState extends SocialState(Set.empty) abstract class TestStateProvider extends SocialStateHandler { @@ -326,8 +328,8 @@ trait OAuth2ProviderSpecContext extends Scope with Mockito with ThrownExpectatio * The HTTP layer mock. */ lazy val httpLayer = { - val m = mock[MockHTTPLayer].smart - m.executionContext returns global + val m = mockSmart[MockHTTPLayer] + when(m.executionContext).thenReturn(global) m } @@ -343,7 +345,7 @@ trait OAuth2ProviderSpecContext extends Scope with Mockito with ThrownExpectatio /** * The OAuth2 state. */ - lazy val state = mock[TestState].smart + lazy val state = mockSmart[TestState] /** * A user state item. @@ -353,7 +355,7 @@ trait OAuth2ProviderSpecContext extends Scope with Mockito with ThrownExpectatio /** * The OAuth2 state provider. */ - lazy val stateProvider = mock[TestStateProvider].smart + lazy val stateProvider = mockSmart[TestStateProvider] /** * The stateful auth info. diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala index 626d32e6..d454c205 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala @@ -19,12 +19,15 @@ import io.github.honeycombcheesecake.play.silhouette.api.util.HTTPLayer import io.github.honeycombcheesecake.play.silhouette.impl.exceptions.UnexpectedResponseException import io.github.honeycombcheesecake.play.silhouette.impl.providers.OpenIDProvider._ import org.specs2.matcher.ThrownExpectations -import org.specs2.mock.Mockito import org.specs2.specification.Scope +import org.mockito.ArgumentMatchers.any +import org.mockito.Mockito._ +import org.mockito.ArgumentCaptor import play.api.mvc.{ AnyContent, AnyContentAsEmpty } import play.api.test.{ FakeHeaders, FakeRequest, WithApplication } import play.mvc.Http.HeaderNames import test.SocialProviderSpec +import test.Helper.mock import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -42,7 +45,7 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] { "fail with an UnexpectedResponseException if redirect URL couldn't be retrieved" in new WithApplication { implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - c.openIDService.redirectURL(any(), any())(any()) returns Future.failed(new Exception("")) + when(c.openIDService.redirectURL(any(), any())(any())).thenReturn(Future.failed(new Exception(""))) failed[UnexpectedResponseException](c.provider.authenticate()) { case e => e.getMessage must startWith(ErrorRedirectURL.format(c.provider.id, "")) @@ -51,7 +54,7 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] { "redirect to provider by using the provider URL" in new WithApplication { implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - c.openIDService.redirectURL(any(), any())(any()) answers { _: Any => Future.successful(c.openIDSettings.providerURL) } + when(c.openIDService.redirectURL(any(), any())(any())).thenAnswer(_ => Future.successful(c.openIDSettings.providerURL)) result(c.provider.authenticate()) { result => status(result) must equalTo(SEE_OTHER) @@ -61,7 +64,7 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] { "redirect to provider by using a openID" in new WithApplication { implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?openID=my.open.id") - c.openIDService.redirectURL(any(), any())(any()) answers { _: Any => Future.successful(c.openIDSettings.providerURL) } + when(c.openIDService.redirectURL(any(), any())(any())).thenAnswer(_ => Future.successful(c.openIDSettings.providerURL)) result(c.provider.authenticate()) { result => status(result) must equalTo(SEE_OTHER) @@ -89,16 +92,18 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] { body = AnyContentAsEmpty, secure = secure) - c.openIDSettings.callbackURL returns callbackURL - c.openIDService.redirectURL(any(), any())(any()) answers { _: Any => Future.successful(c.openIDSettings.providerURL) } + when(c.openIDSettings.callbackURL).thenReturn(callbackURL) + when(c.openIDService.redirectURL(any(), any())(any())).thenAnswer(_ => Future.successful(c.openIDSettings.providerURL)) await(c.provider.authenticate()) - there was one(c.openIDService).redirectURL(any(), ===(resolvedCallbackURL))(any()) + val argument = ArgumentCaptor.forClass(classOf[String]) + verify(c.openIDService).redirectURL(any(), argument.capture())(any()) + assert(argument.getValue() === resolvedCallbackURL) } "fail with an UnexpectedResponseException if auth info cannot be retrieved" in new WithApplication { implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Mode + "=id_res") - c.openIDService.verifiedID(any(), any()) returns Future.failed(new Exception("")) + when(c.openIDService.verifiedID(any(), any())).thenReturn(Future.failed(new Exception(""))) failed[UnexpectedResponseException](c.provider.authenticate()) { case e => e.getMessage must startWith(ErrorVerification.format(c.provider.id, "")) @@ -107,7 +112,7 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] { "return the auth info" in new WithApplication { implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Mode + "=id_res") - c.openIDService.verifiedID(any(), any()) answers { _: Any => Future.successful(c.openIDInfo) } + when(c.openIDService.verifiedID(any(), any())).thenAnswer(_ => Future.successful(c.openIDInfo)) authInfo(c.provider.authenticate())(_ must be equalTo c.openIDInfo) } @@ -131,14 +136,14 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] { /** * Context for the OpenIDProviderSpec. */ -trait OpenIDProviderSpecContext extends Scope with Mockito with ThrownExpectations { +trait OpenIDProviderSpecContext extends Scope with ThrownExpectations { /** * The HTTP layer mock. */ lazy val httpLayer = { val m = mock[HTTPLayer] - m.executionContext returns global + when(m.executionContext).thenReturn(global) m } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/PasswordProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/PasswordProviderSpec.scala index 07bd244f..6f4fe2d5 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/PasswordProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/PasswordProviderSpec.scala @@ -17,14 +17,16 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers import io.github.honeycombcheesecake.play.silhouette.api.repositories.AuthInfoRepository import io.github.honeycombcheesecake.play.silhouette.api.util._ -import org.specs2.mock.Mockito import org.specs2.specification.Scope +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any +import test.Helper.mock import play.api.test.PlaySpecification /** * Abstract test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.providers.PasswordProvider]] based class. */ -trait PasswordProviderSpec extends PlaySpecification with Mockito { +trait PasswordProviderSpec extends PlaySpecification { /** * The context. @@ -59,11 +61,11 @@ trait PasswordProviderSpec extends PlaySpecification with Mockito { */ private def hasher(id: String) = { val h = mock[PasswordHasher] - h.id returns id - h.isSuitable(any()) answers { p: Any => - p.asInstanceOf[PasswordInfo].hasher == h.id + when(h.id).thenReturn(id) + when(h.isSuitable(any())).thenAnswer { p => + p.getArgument(0).asInstanceOf[PasswordInfo].hasher == h.id } - h.isDeprecated(any()) returns Some(false) + when(h.isDeprecated(any())).thenReturn(Some(false)) h } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala index 607d665f..7b10feaf 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala @@ -18,14 +18,15 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.TwitterProvider import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.{ GoogleProvider, FacebookProvider } import io.github.honeycombcheesecake.play.silhouette.impl.providers.openid.YahooProvider -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.test.PlaySpecification +import org.mockito.Mockito._ +import test.Helper.mock /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialProviderRegistry]] class. */ -class SocialProviderRegistrySpec extends PlaySpecification with Mockito { +class SocialProviderRegistrySpec extends PlaySpecification { "The `get` method" should { "return a provider by its type" in new Context { @@ -79,11 +80,11 @@ class SocialProviderRegistrySpec extends PlaySpecification with Mockito { */ val providers = { val facebook = mock[FacebookProvider] - facebook.id returns FacebookProvider.ID + when(facebook.id).thenReturn(FacebookProvider.ID) val google = mock[GoogleProvider] - google.id returns GoogleProvider.ID + when(google.id).thenReturn(GoogleProvider.ID) val twitter = mock[TwitterProvider] - twitter.id returns TwitterProvider.ID + when(twitter.id).thenReturn(TwitterProvider.ID) Seq( facebook, diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala index 1a858d9d..3ec4d809 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala @@ -34,7 +34,7 @@ import scala.concurrent.{ ExecutionContext, Future } /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.FacebookProvider]] class which uses a custom social profile. */ -class FacebookProviderSpec extends OAuth2ProviderSpec { +class FacebookProviderSpec extends OAuth2ProviderSpec with org.specs2.mock.Mockito { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { @@ -164,7 +164,7 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { /** * The OAuth2 settings. */ - override lazy val oAuthSettings = spy(OAuth2Settings( + override lazy val oAuthSettings = org.mockito.Mockito.spy(OAuth2Settings( authorizationURL = Some("https://graph.facebook.com/oauth/authorize"), accessTokenURL = "https://graph.facebook.com/oauth/access_token", redirectURL = Some("https://www.mohiva.com"), diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala index 583f5169..0dc7e66f 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala @@ -22,7 +22,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialProfil import io.github.honeycombcheesecake.play.silhouette.impl.providers._ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.LinkedInProvider._ import play.api.test.WithApplication +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any import test.Helper +import test.Helper.mock import scala.concurrent.Future @@ -39,7 +42,7 @@ class LinkedInProviderSpec extends OAuth1ProviderSpec { val s = provider.withSettings(overrideSettingsFunction) s.settings.requestTokenURL must be equalTo "new-request-token-url" - there was one(oAuthService).withSettings(overrideSettingsFunction) + verify(oAuthService).withSettings(overrideSettingsFunction) } } @@ -47,10 +50,10 @@ class LinkedInProviderSpec extends OAuth1ProviderSpec { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json returns Helper.loadJson("providers/oauth1/linkedin.error.json") - httpLayer.url(API) returns wsRequest + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/linkedin.error.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -66,10 +69,10 @@ class LinkedInProviderSpec extends OAuth1ProviderSpec { "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json throws new RuntimeException("") - httpLayer.url(API) returns wsRequest + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(httpLayer.url(API)).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -80,24 +83,24 @@ class LinkedInProviderSpec extends OAuth1ProviderSpec { val url = "https://custom.api.url" val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - oAuthSettings.apiURL returns Some(url) - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json returns Helper.loadJson("providers/oauth1/linkedin.success.json") - httpLayer.url(url) returns wsRequest + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/linkedin.success.json")) + when(httpLayer.url(url)).thenReturn(wsRequest) await(provider.retrieveProfile(oAuthInfo)) - there was one(httpLayer).url(url) + verify(httpLayer).url(url) } "return the social profile" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json returns Helper.loadJson("providers/oauth1/linkedin.success.json") - httpLayer.url(API) returns wsRequest + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/linkedin.success.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo)) { p => p must be equalTo CommonSocialProfile( diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala index a7dcf7fc..5b943df6 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala @@ -22,7 +22,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialProfil import io.github.honeycombcheesecake.play.silhouette.impl.providers._ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.TwitterProvider._ import play.api.test.WithApplication +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any import test.Helper +import test.Helper.mock import scala.concurrent.Future @@ -39,7 +42,7 @@ class TwitterProviderSpec extends OAuth1ProviderSpec { val s = provider.withSettings(overrideSettingsFunction) s.settings.requestTokenURL must be equalTo "new-request-token-url" - there was one(oAuthService).withSettings(overrideSettingsFunction) + verify(oAuthService).withSettings(overrideSettingsFunction) } } @@ -47,10 +50,10 @@ class TwitterProviderSpec extends OAuth1ProviderSpec { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json returns Helper.loadJson("providers/oauth1/twitter.error.json") - httpLayer.url(API) returns wsRequest + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.error.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -63,10 +66,10 @@ class TwitterProviderSpec extends OAuth1ProviderSpec { "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json throws new RuntimeException("") - httpLayer.url(API) returns wsRequest + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(httpLayer.url(API)).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -77,24 +80,24 @@ class TwitterProviderSpec extends OAuth1ProviderSpec { val url = "https://custom.api.url" val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - oAuthSettings.apiURL returns Some(url) - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json returns Helper.loadJson("providers/oauth1/twitter.with.email.json") - httpLayer.url(url) returns wsRequest + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.with.email.json")) + when(httpLayer.url(url)).thenReturn(wsRequest) await(provider.retrieveProfile(oAuthInfo)) - there was one(httpLayer).url(url) + verify(httpLayer).url(url) } "return the social profile" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json returns Helper.loadJson("providers/oauth1/twitter.success.json") - httpLayer.url(API) returns wsRequest + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.success.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo)) { p => p must be equalTo CommonSocialProfile( @@ -107,10 +110,10 @@ class TwitterProviderSpec extends OAuth1ProviderSpec { "return the social profile with email" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json returns Helper.loadJson("providers/oauth1/twitter.with.email.json") - httpLayer.url(API) returns wsRequest + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.with.email.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo)) { p => p must be equalTo CommonSocialProfile( @@ -137,7 +140,7 @@ class TwitterProviderSpec extends OAuth1ProviderSpec { /** * The OAuth1 settings. */ - override lazy val oAuthSettings = spy(OAuth1Settings( + override lazy val oAuthSettings = org.mockito.Mockito.spy(OAuth1Settings( requestTokenURL = "https://twitter.com/oauth/request_token", accessTokenURL = "https://twitter.com/oauth/access_token", authorizationURL = "https://twitter.com/oauth/authenticate", diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala index bfb7ce56..18c0b995 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala @@ -22,7 +22,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialProfil import io.github.honeycombcheesecake.play.silhouette.impl.providers._ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.XingProvider._ import play.api.test.WithApplication +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any import test.Helper +import test.Helper.mock import scala.concurrent.Future @@ -39,7 +42,7 @@ class XingProviderSpec extends OAuth1ProviderSpec { val s = provider.withSettings(overrideSettingsFunction) s.settings.requestTokenURL must be equalTo "new-request-token-url" - there was one(oAuthService).withSettings(overrideSettingsFunction) + verify(oAuthService).withSettings(overrideSettingsFunction) } } @@ -47,10 +50,10 @@ class XingProviderSpec extends OAuth1ProviderSpec { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json returns Helper.loadJson("providers/oauth1/xing.error.json") - httpLayer.url(API) returns wsRequest + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/xing.error.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -63,10 +66,10 @@ class XingProviderSpec extends OAuth1ProviderSpec { "throw ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json throws new RuntimeException("") - httpLayer.url(API) returns wsRequest + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(httpLayer.url(API)).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -77,24 +80,24 @@ class XingProviderSpec extends OAuth1ProviderSpec { val url = "https://custom.api.url" val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - oAuthSettings.apiURL returns Some(url) - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json returns Helper.loadJson("providers/oauth1/xing.success.json") - httpLayer.url(url) returns wsRequest + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/xing.success.json")) + when(httpLayer.url(url)).thenReturn(wsRequest) await(provider.retrieveProfile(oAuthInfo)) - there was one(httpLayer).url(url) + verify(httpLayer).url(url) } "return the social profile" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsRequest.sign(any) returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - wsResponse.json returns Helper.loadJson("providers/oauth1/xing.success.json") - httpLayer.url(API) returns wsRequest + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/xing.success.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo)) { p => p must be equalTo CommonSocialProfile( diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala index a00dfe85..515c31ea 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala @@ -25,10 +25,12 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.secre import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.secrets.CookieSecretProvider._ import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.JsonMatchers -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.mvc.{ AnyContentAsEmpty, Cookie, Results } import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any +import test.Helper.mockSmart import java.time.{ ZoneId, ZonedDateTime } import scala.concurrent.ExecutionContext.Implicits.global @@ -40,7 +42,7 @@ import scala.util.{ Failure, Success } /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.secrets.CookieSecret]] class. */ -class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers with NoLanguageFeatures { +class CookieSecretSpec extends PlaySpecification with JsonMatchers with NoLanguageFeatures { "The `isExpired` method of the secret" should { "return true if the secret is expired" in new Context { @@ -56,13 +58,13 @@ class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers "sign the cookie" in new WithApplication with Context { serialize(secret, signer, crypter) - there was one(signer).sign(any()) + verify(signer).sign(any()) } "encrypt the cookie" in new WithApplication with Context { serialize(secret, signer, crypter) - there was one(crypter).encrypt(any()) + verify(crypter).encrypt(any()) } } @@ -82,7 +84,7 @@ class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers } "throw an OAuth1TokenSecretException if a secret is badly signed" in new WithApplication with Context { - signer.extract(any()) returns Failure(new Exception("Bad signature")) + when(signer.extract(any())).thenReturn(Failure(new Exception("Bad signature"))) val value = serialize(secret, signer, crypter) val msg = Pattern.quote(InvalidCookieSignature) @@ -104,7 +106,7 @@ class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() val dateTime = ZonedDateTime.of(2014, 8, 8, 0, 0, 0, 0, ZoneId.systemDefault) - clock.now returns dateTime + when(clock.now).thenReturn(dateTime) val s = await(provider.build(oAuthInfo)) @@ -149,7 +151,7 @@ class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers } "throw an OAuth1TokenSecretException if client secret is badly signed" in new WithApplication with Context { - signer.extract(any()) returns Failure(new Exception("Bad signature")) + when(signer.extract(any())).thenReturn(Failure(new Exception("Bad signature"))) implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(secret, signer, crypter))) @@ -189,7 +191,7 @@ class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers /** * The clock implementation. */ - lazy val clock: Clock = mock[Clock].smart + lazy val clock: Clock = mockSmart[Clock] /** * The settings. @@ -205,9 +207,9 @@ class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers * none cookie values in a cookie. */ lazy val crypter = { - val c = mock[Crypter].smart - c.encrypt(any()) answers { p: Any => Base64.encode(p.asInstanceOf[String]) } - c.decrypt(any()) answers { p: Any => Base64.decode(p.asInstanceOf[String]) } + val c = mockSmart[Crypter] + when(c.encrypt(any())).thenAnswer(p => Base64.encode(p.getArgument(0).asInstanceOf[String])) + when(c.decrypt(any())).thenAnswer(p => Base64.decode(p.getArgument(0).asInstanceOf[String])) c } @@ -217,9 +219,9 @@ class CookieSecretSpec extends PlaySpecification with Mockito with JsonMatchers * The signer returns the same value as passed to the methods. This is enough for testing. */ lazy val signer = { - val c = mock[Signer].smart - c.sign(any()) answers { p: Any => p.asInstanceOf[String] } - c.extract(any()) answers { p: Any => Success(p.asInstanceOf[String]) } + val c = mockSmart[Signer] + when(c.sign(any())).thenAnswer(_.getArgument(0).asInstanceOf[String]) + when(c.extract(any())).thenAnswer(p => Success(p.getArgument(0).asInstanceOf[String])) c } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/services/PlayOAuth1ServiceSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/services/PlayOAuth1ServiceSpec.scala index 16233d04..7ff4abe9 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/services/PlayOAuth1ServiceSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/services/PlayOAuth1ServiceSpec.scala @@ -16,19 +16,19 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.services import io.github.honeycombcheesecake.play.silhouette.impl.providers.{ OAuth1Info, OAuth1Settings } -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.libs.oauth.{ OAuth, RequestToken } import play.api.libs.ws.WSSignatureCalculator import play.api.test.{ PlaySpecification, WithApplication } import play.shaded.oauth.oauth.signpost.exception.{ OAuthException, OAuthMessageSignerException } +import org.mockito.Mockito._ import scala.concurrent.ExecutionContext.Implicits.global /** * Test case for the [[PlayOAuth1Service]] class. */ -class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito { +class PlayOAuth1ServiceSpec extends PlaySpecification { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { @@ -48,13 +48,13 @@ class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito { "The `use10a` method" should { "return true if the safer 1.0a specification will be used" in new Context { - oauth.use10a returns true + when(oauth.use10a).thenReturn(true) service.use10a must beTrue } "return false if the unsafer 1.0 specification will be used" in new Context { - oauth.use10a returns false + when(oauth.use10a).thenReturn(false) service.use10a must beFalse } @@ -62,13 +62,13 @@ class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito { "The `retrieveRequestToken` method" should { "throw exception if the token couldn't be retrieved" in new Context { - oauth.retrieveRequestToken(settings.callbackURL) returns Left(new OAuthMessageSignerException("")) + when(oauth.retrieveRequestToken(settings.callbackURL)).thenReturn(Left(new OAuthMessageSignerException(""))) await(service.retrieveRequestToken(settings.callbackURL)) must throwA[OAuthException] } "return request token" in new Context { - oauth.retrieveRequestToken(settings.callbackURL) returns Right(token) + when(oauth.retrieveRequestToken(settings.callbackURL)).thenReturn(Right(token)) await(service.retrieveRequestToken(settings.callbackURL)) must be equalTo info } @@ -76,13 +76,13 @@ class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito { "The `retrieveAccessToken` method" should { "throw Exception if the token couldn't be retrieved" in new Context { - oauth.retrieveAccessToken(token, "") returns Left(new OAuthMessageSignerException("")) + when(oauth.retrieveAccessToken(token, "")).thenReturn(Left(new OAuthMessageSignerException(""))) await(service.retrieveAccessToken(info, "")) must throwA[OAuthException] } "return access token" in new Context { - oauth.retrieveAccessToken(token, "") returns Right(token) + when(oauth.retrieveAccessToken(token, "")).thenReturn(Right(token)) await(service.retrieveAccessToken(info, "")) must be equalTo info } @@ -90,7 +90,7 @@ class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito { "The `redirectUrl` method" should { "return the redirect Url" in new Context { - oauth.redirectUrl("token") returns "http://redirect.url" + when(oauth.redirectUrl("token")).thenReturn("http://redirect.url") service.redirectUrl("token") must be equalTo "http://redirect.url" } @@ -98,7 +98,7 @@ class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito { "The `sign` method" should { "return the signature calculator" in new Context { - oauth.info returns PlayOAuth1Service.serviceInfo(settings) + when(oauth.info).thenReturn(PlayOAuth1Service.serviceInfo(settings)) service.sign(info) must beAnInstanceOf[WSSignatureCalculator] } @@ -133,7 +133,7 @@ class PlayOAuth1ServiceSpec extends PlaySpecification with Mockito { /** * A mock of the Play Framework OAuth implementation. */ - lazy val oauth: OAuth = mock[OAuth] + lazy val oauth: OAuth = mock(classOf[OAuth]) /** * The service to test. diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala index 94b1fd7f..70954396 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala @@ -24,7 +24,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Auth0 import play.api.mvc.AnyContentAsEmpty import play.api.libs.json.Json import play.api.test.{ FakeRequest, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ import test.Helper +import test.Helper.mock import scala.concurrent.{ ExecutionContext, Future } @@ -48,13 +51,13 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 401 - wsResponse.body returns "Unauthorized" - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) @@ -66,13 +69,13 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns Json.obj() - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) @@ -84,13 +87,13 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) } @@ -101,15 +104,15 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) - stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider - state.items returns Set(userStateItem) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) } @@ -120,10 +123,10 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 400 - wsRequest.get() returns Future.successful(wsResponse) - wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}")) returns wsRequest - httpLayer.url(oAuthSettings.apiURL.get) returns wsRequest + when(wsResponse.status).thenReturn(400) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}"))).thenReturn(wsRequest) + when(httpLayer.url(oAuthSettings.apiURL.get)).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfoObject)) { case e => e.getMessage must equalTo(GenericHttpStatusProfileError.format(provider.id, 400)) @@ -134,11 +137,11 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 500 - wsResponse.json throws new RuntimeException("") - wsRequest.get() returns Future.successful(wsResponse) - wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}")) returns wsRequest - httpLayer.url(oAuthSettings.apiURL.get) returns wsRequest + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}"))).thenReturn(wsRequest) + when(httpLayer.url(oAuthSettings.apiURL.get)).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo("[Silhouette][auth0] error retrieving profile information") @@ -150,11 +153,11 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { val wsResponse = mock[MockWSRequest#Response] val userProfile = Helper.loadJson(Auth0UserProfileJson) - wsResponse.status returns 200 - wsResponse.json returns userProfile - wsRequest.get() returns Future.successful(wsResponse) - wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}")) returns wsRequest - httpLayer.url(oAuthSettings.apiURL.get) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(userProfile) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}"))).thenReturn(wsRequest) + when(httpLayer.url(oAuthSettings.apiURL.get)).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala index 5377e49f..5df9a180 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala @@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Dropb import play.api.mvc.AnyContentAsEmpty import play.api.libs.json.Json import play.api.test.{ FakeRequest, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ import test.Helper +import test.Helper.mock import scala.concurrent.{ ExecutionContext, Future } @@ -49,13 +52,13 @@ class DropboxProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 401 - wsResponse.body returns "Unauthorized" - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) @@ -66,13 +69,13 @@ class DropboxProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns Json.obj() - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) @@ -83,13 +86,13 @@ class DropboxProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) } @@ -100,15 +103,15 @@ class DropboxProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) - stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider - state.items returns Set(userStateItem) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) } @@ -119,11 +122,11 @@ class DropboxProviderSpec extends OAuth2ProviderSpec { val authInfo = oAuthInfo.as[OAuth2Info] val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.json returns Helper.loadJson("providers/oauth2/dropbox.error.json") - wsResponse.status returns 400 - wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/dropbox.error.json")) + when(wsResponse.status).thenReturn(400) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -137,11 +140,11 @@ class DropboxProviderSpec extends OAuth2ProviderSpec { val authInfo = oAuthInfo.as[OAuth2Info] val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 500 - wsResponse.json throws new RuntimeException("") - wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -153,27 +156,27 @@ class DropboxProviderSpec extends OAuth2ProviderSpec { val authInfo = oAuthInfo.as[OAuth2Info] val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - oAuthSettings.apiURL returns Some(url) - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/dropbox.success.json") - wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(url) returns wsRequest + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/dropbox.success.json")) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url)).thenReturn(wsRequest) await(provider.retrieveProfile(authInfo)) - there was one(httpLayer).url(url) + verify(httpLayer).url(url) } "return the social profile" in new WithApplication with Context { val authInfo = oAuthInfo.as[OAuth2Info] val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/dropbox.success.json") - wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/dropbox.success.json")) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile(authInfo)) { p => p must be equalTo CommonSocialProfile( diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala index f9ec2591..d0950016 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala @@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Faceb import play.api.mvc.AnyContentAsEmpty import play.api.libs.json.Json import play.api.test.{ FakeRequest, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ import test.Helper +import test.Helper.mock import scala.concurrent.{ ExecutionContext, Future } @@ -49,13 +52,13 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 401 - wsResponse.body returns "Unauthorized" - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) @@ -66,13 +69,13 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns Json.obj() - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) @@ -83,13 +86,13 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) } @@ -100,15 +103,15 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) - stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider - state.items returns Set(userStateItem) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) } @@ -118,10 +121,10 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 400 - wsResponse.json returns Helper.loadJson("providers/oauth2/facebook.error.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(400) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/facebook.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -135,10 +138,10 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 500 - wsResponse.json throws new RuntimeException("") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -149,24 +152,24 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { val url = "https://custom.api.url?access_token=%s" val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - oAuthSettings.apiURL returns Some(url) - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/facebook.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(url.format("my.access.token")) returns wsRequest + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/facebook.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - there was one(httpLayer).url(url.format("my.access.token")) + verify(httpLayer).url(url.format("my.access.token")) } "return the social profile" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/facebook.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/facebook.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala index d2fe157a..ea9ca526 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala @@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Fours import play.api.mvc.AnyContentAsEmpty import play.api.libs.json.Json import play.api.test.{ FakeRequest, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ import test.Helper +import test.Helper.mock import scala.concurrent.{ ExecutionContext, Future } @@ -49,13 +52,13 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 401 - wsResponse.body returns "Unauthorized" - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) @@ -66,13 +69,13 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns Json.obj() - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) @@ -83,13 +86,13 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) } @@ -100,15 +103,15 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) - stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider - state.items returns Set(userStateItem) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) } @@ -118,10 +121,10 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 400 - wsResponse.json returns Helper.loadJson("providers/oauth2/foursquare.error.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token", DefaultAPIVersion)) returns wsRequest + when(wsResponse.status).thenReturn(400) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -135,10 +138,10 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 500 - wsResponse.json throws new RuntimeException("") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token", DefaultAPIVersion)) returns wsRequest + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -149,24 +152,24 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { val url = "https://custom.api.url?access_token=%s" val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - oAuthSettings.apiURL returns Some(url) - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/foursquare.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(url.format("my.access.token")) returns wsRequest + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - there was one(httpLayer).url(url.format("my.access.token")) + verify(httpLayer).url(url.format("my.access.token")) } "return the social profile" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/foursquare.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token", DefaultAPIVersion)) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( @@ -181,10 +184,10 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { "return the social profile if API is deprecated" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/foursquare.deprecated.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token", DefaultAPIVersion)) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.deprecated.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( @@ -201,10 +204,10 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/foursquare.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token", "20120101")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token", "20120101"))).thenReturn(wsRequest) profile(provider.withSettings(_.copy(customProperties = customProperties)) .retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => @@ -222,10 +225,10 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/foursquare.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token", DefaultAPIVersion)) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) profile(provider.withSettings(_.copy(customProperties = customProperties)) .retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala index 3da08694..e1ec3ce7 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala @@ -26,7 +26,10 @@ import play.api.http.HeaderNames import play.api.mvc.AnyContentAsEmpty import play.api.libs.json.Json import play.api.test.{ FakeRequest, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ import test.Helper +import test.Helper.mock import scala.concurrent.{ ExecutionContext, Future } @@ -50,13 +53,13 @@ class GitHubProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 401 - wsResponse.body returns "Unauthorized" - wsRequest.withHttpHeaders(HeaderNames.ACCEPT -> "application/json") returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(HeaderNames.ACCEPT -> "application/json")).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) @@ -67,13 +70,13 @@ class GitHubProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns Json.obj() - wsRequest.withHttpHeaders(HeaderNames.ACCEPT -> "application/json") returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(HeaderNames.ACCEPT -> "application/json")).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) @@ -84,13 +87,13 @@ class GitHubProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) } @@ -101,15 +104,15 @@ class GitHubProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) - stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider - state.items returns Set(userStateItem) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) } @@ -120,11 +123,11 @@ class GitHubProviderSpec extends OAuth2ProviderSpec { val authInfo = oAuthInfo.as[OAuth2Info] val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 400 - wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest - wsResponse.json returns Helper.loadJson("providers/oauth2/github.error.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API) returns wsRequest + when(wsResponse.status).thenReturn(400) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/github.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API)).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -138,11 +141,11 @@ class GitHubProviderSpec extends OAuth2ProviderSpec { val authInfo = oAuthInfo.as[OAuth2Info] val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 500 - wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest - wsResponse.json throws new RuntimeException("") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API) returns wsRequest + when(wsResponse.status).thenReturn(500) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API)).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -154,27 +157,27 @@ class GitHubProviderSpec extends OAuth2ProviderSpec { val authInfo = oAuthInfo.as[OAuth2Info] val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - oAuthSettings.apiURL returns Some(url) - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/github.success.json") - wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(url) returns wsRequest + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/github.success.json")) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url)).thenReturn(wsRequest) await(provider.retrieveProfile(authInfo)) - there was one(httpLayer).url(url) + verify(httpLayer).url(url) } "return the social profile" in new WithApplication with Context { val authInfo = oAuthInfo.as[OAuth2Info] val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/github.success.json") - wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}") returns wsRequest - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/github.success.json")) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API)).thenReturn(wsRequest) profile(provider.retrieveProfile(authInfo)) { p => p must be equalTo CommonSocialProfile( diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala index f6df2584..92a90687 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala @@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.GitLa import play.api.libs.json.Json import play.api.mvc.AnyContentAsEmpty import play.api.test.{ FakeRequest, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ import test.Helper +import test.Helper.mock import scala.concurrent.{ ExecutionContext, Future } @@ -49,13 +52,13 @@ class GitLabProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 401 - wsResponse.body returns "Unauthorized" - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) @@ -66,13 +69,13 @@ class GitLabProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns Json.obj() - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) @@ -83,13 +86,13 @@ class GitLabProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) } @@ -100,15 +103,15 @@ class GitLabProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) - stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider - state.items returns Set(userStateItem) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) } @@ -118,10 +121,10 @@ class GitLabProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 400 - wsResponse.json returns Helper.loadJson("providers/oauth2/gitlab.error.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(400) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/gitlab.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -133,10 +136,10 @@ class GitLabProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 500 - wsResponse.json throws new RuntimeException("") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -147,24 +150,24 @@ class GitLabProviderSpec extends OAuth2ProviderSpec { val url = "https://custom.api.url?access_token=%s" val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - oAuthSettings.apiURL returns Some(url) - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/gitlab.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(url.format("my.access.token")) returns wsRequest + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/gitlab.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - there was one(httpLayer).url(url.format("my.access.token")) + verify(httpLayer).url(url.format("my.access.token")) } "return the social profile" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/gitlab.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/gitlab.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala index 5b5243bc..26365c27 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala @@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Googl import play.api.libs.json.Json import play.api.mvc.AnyContentAsEmpty import play.api.test.{ FakeRequest, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ import test.Helper +import test.Helper.mock import scala.concurrent.{ ExecutionContext, Future } @@ -49,13 +52,13 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 401 - wsResponse.body returns "Unauthorized" - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) @@ -66,13 +69,13 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns Json.obj() - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) @@ -83,13 +86,13 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) } @@ -100,15 +103,15 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) - stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider - state.items returns Set(userStateItem) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) } @@ -118,10 +121,10 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 401 - wsResponse.json returns Helper.loadJson("providers/oauth2/google.error.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(401) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -134,10 +137,10 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if API returns missing People API config" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 403 - wsResponse.json returns Helper.loadJson("providers/oauth2/google.error.api.missing.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(403) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.error.api.missing.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) val apiErrMsg = "People API has not been used in project 1234567890 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/people.googleapis.com/overview?project=1234567890 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry." @@ -152,10 +155,10 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 500 - wsResponse.json throws new RuntimeException("") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -166,24 +169,24 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { val url = "https://custom.api.url?access_token=%s" val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - oAuthSettings.apiURL returns Some(url) - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/google.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(url.format("my.access.token")) returns wsRequest + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - there was one(httpLayer).url(url.format("my.access.token")) + verify(httpLayer).url(url.format("my.access.token")) } "return the social profile with an email" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/google.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( @@ -199,10 +202,10 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { "return the social profile without an email" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/google.without.email.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.without.email.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( @@ -217,10 +220,10 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { "return the social profile with an avatar url" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/google.img.non-default.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.img.non-default.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( @@ -235,10 +238,10 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { "return the social profile without an avatar url" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/google.img.default.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.img.default.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala index 31efe11e..a6289fd2 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala @@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Insta import play.api.libs.json.Json import play.api.mvc.AnyContentAsEmpty import play.api.test.{ FakeRequest, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ import test.Helper +import test.Helper.mock import scala.concurrent.{ ExecutionContext, Future } @@ -49,13 +52,13 @@ class InstagramProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 401 - wsResponse.body returns "Unauthorized" - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) @@ -66,13 +69,13 @@ class InstagramProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns Json.obj() - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) @@ -83,13 +86,13 @@ class InstagramProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) } @@ -100,15 +103,15 @@ class InstagramProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) - stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider - state.items returns Set(userStateItem) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) } @@ -118,10 +121,10 @@ class InstagramProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 400 - wsResponse.json returns Helper.loadJson("providers/oauth2/instagram.error.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(400) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/instagram.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -135,10 +138,10 @@ class InstagramProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 500 - wsResponse.json throws new RuntimeException("") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -149,24 +152,24 @@ class InstagramProviderSpec extends OAuth2ProviderSpec { val url = "https://custom.api.url?access_token=%s" val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - oAuthSettings.apiURL returns Some(url) - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/instagram.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(url.format("my.access.token")) returns wsRequest + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/instagram.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - there was one(httpLayer).url(url.format("my.access.token")) + verify(httpLayer).url(url.format("my.access.token")) } "return the social profile" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/instagram.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/instagram.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala index 07273cb8..ec02ea7d 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala @@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.Linke import play.api.libs.json.Json import play.api.mvc.AnyContentAsEmpty import play.api.test.{ FakeRequest, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ import test.Helper +import test.Helper.mock import scala.concurrent.{ ExecutionContext, Future } @@ -49,13 +52,13 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 401 - wsResponse.body returns "Unauthorized" - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) @@ -66,13 +69,13 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns Json.obj() - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) @@ -83,13 +86,13 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) } @@ -100,15 +103,15 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) - stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider - state.items returns Set(userStateItem) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) } @@ -118,10 +121,10 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 401 - wsResponse.json returns Helper.loadJson("providers/oauth2/linkedin.error.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(401) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) mockEmailAndPhoto(httpLayer) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -137,10 +140,10 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 500 - wsResponse.json throws new RuntimeException("") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) mockEmailAndPhoto(httpLayer) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -151,32 +154,32 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec { val url = "https://custom.api.url?access_token=%s" val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - oAuthSettings.apiURL returns Some(url) - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/linkedin.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(url.format("my.access.token")) returns wsRequest + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) mockEmailAndPhoto(httpLayer) await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - there was one(httpLayer).url(url.format("my.access.token")) + verify(httpLayer).url(url.format("my.access.token")) } def mockEmailAndPhoto(httpLayer: MockHTTPLayer) = { // Email val wsRequestEmail = mock[MockWSRequest] val wsResponseEmail = mock[MockWSRequest#Response] - wsResponseEmail.status returns 200 - wsResponseEmail.json returns Helper.loadJson("providers/oauth2/linkedin.email.json") - wsRequestEmail.get() returns Future.successful(wsResponseEmail) - httpLayer.url(EMAIL.format("my.access.token")) returns wsRequestEmail + when(wsResponseEmail.status).thenReturn(200) + when(wsResponseEmail.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.email.json")) + when(wsRequestEmail.get()).thenReturn(Future.successful(wsResponseEmail)) + when(httpLayer.url(EMAIL.format("my.access.token"))).thenReturn(wsRequestEmail) // Photo val wsRequestPhoto = mock[MockWSRequest] val wsResponsePhoto = mock[MockWSRequest#Response] - wsResponsePhoto.status returns 200 - wsResponsePhoto.json returns Helper.loadJson("providers/oauth2/linkedin.photo.json") - wsRequestPhoto.get() returns Future.successful(wsResponsePhoto) - httpLayer.url(PHOTO.format("my.access.token")) returns wsRequestPhoto + when(wsResponsePhoto.status).thenReturn(200) + when(wsResponsePhoto.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.photo.json")) + when(wsRequestPhoto.get()).thenReturn(Future.successful(wsResponsePhoto)) + when(httpLayer.url(PHOTO.format("my.access.token"))).thenReturn(wsRequestPhoto) } @@ -184,10 +187,10 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec { // Basic profile val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/linkedin.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) mockEmailAndPhoto(httpLayer) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala index e0897735..56cddf8d 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala @@ -25,7 +25,10 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.VKPro import play.api.libs.json.{ JsObject, Json } import play.api.mvc.AnyContentAsEmpty import play.api.test.{ FakeRequest, WithApplication } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers._ import test.Helper +import test.Helper.mock import scala.concurrent.{ ExecutionContext, Future } @@ -49,13 +52,13 @@ class VKProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 401 - wsResponse.body returns "Unauthorized" - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) @@ -66,13 +69,13 @@ class VKProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns Json.obj() - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) @@ -83,13 +86,13 @@ class VKProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) } @@ -100,15 +103,15 @@ class VKProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) - stateProvider.withHandler(any[SocialStateItemHandler]) returns stateProvider - state.items returns Set(userStateItem) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) } @@ -118,10 +121,10 @@ class VKProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 400 - wsResponse.json returns Helper.loadJson("providers/oauth2/vk.error.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(400) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -134,10 +137,10 @@ class VKProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 500 - wsResponse.json throws new RuntimeException("") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -148,24 +151,24 @@ class VKProviderSpec extends OAuth2ProviderSpec { val url = "https://custom.api.url?access_token=%s" val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - oAuthSettings.apiURL returns Some(url) - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/vk.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(url.format("my.access.token")) returns wsRequest + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - there was one(httpLayer).url(url.format("my.access.token")) + verify(httpLayer).url(url.format("my.access.token")) } "return the social profile with email" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/vk.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( @@ -180,10 +183,10 @@ class VKProviderSpec extends OAuth2ProviderSpec { "return the social profile from response without photo" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/vk.success.without.photo.json").as[JsObject] - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.without.photo.json").as[JsObject]) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( @@ -198,10 +201,10 @@ class VKProviderSpec extends OAuth2ProviderSpec { "return the social profile from deprecated API response" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/vk.success.deprecated.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.deprecated.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( @@ -216,10 +219,10 @@ class VKProviderSpec extends OAuth2ProviderSpec { "return the social profile without email" in new WithApplication with Context { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/oauth2/vk.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile((oAuthInfo.as[JsObject] - "email").as[OAuth2Info])) { p => p must be equalTo CommonSocialProfile( diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala index ff97f310..6127b19e 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala @@ -18,6 +18,7 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers.openid import io.github.honeycombcheesecake.play.silhouette.api.LoginInfo import io.github.honeycombcheesecake.play.silhouette.impl.providers._ import play.api.test.WithApplication +import org.mockito.Mockito._ /** * Test case for the [[SteamProvider]] class. @@ -32,7 +33,7 @@ class SteamProviderSpec extends OpenIDProviderSpec { val s = provider.withSettings(overrideSettingsFunction) s.settings.providerURL must be equalTo "new-provider-url" - there was one(openIDService).withSettings(overrideSettingsFunction) + verify(openIDService).withSettings(overrideSettingsFunction) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala index 8c5581ed..fb654ebb 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala @@ -18,6 +18,7 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers.openid import io.github.honeycombcheesecake.play.silhouette.api.LoginInfo import io.github.honeycombcheesecake.play.silhouette.impl.providers._ import play.api.test.WithApplication +import org.mockito.Mockito._ /** * Test case for the [[YahooProvider]] class. @@ -32,7 +33,7 @@ class YahooProviderSpec extends OpenIDProviderSpec { val s = provider.withSettings(overrideSettingsFunction) s.settings.providerURL must be equalTo "new-provider-url" - there was one(openIDService).withSettings(overrideSettingsFunction) + verify(openIDService).withSettings(overrideSettingsFunction) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala index d4c232d3..038916ed 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala @@ -2,12 +2,12 @@ package io.github.honeycombcheesecake.play.silhouette.impl.providers.openid.serv import io.github.honeycombcheesecake.play.silhouette.impl.providers.OpenIDSettings import io.github.honeycombcheesecake.play.silhouette.impl.providers.openid.services.PlayOpenIDService -import org.specs2.mock.Mockito import org.specs2.specification.Scope +import org.mockito.Mockito.mock import play.api.libs.openid.OpenIdClient import play.api.test.{ PlaySpecification, WithApplication } -class PlayOpenIDServiceSpec extends PlaySpecification with Mockito { +class PlayOpenIDServiceSpec extends PlaySpecification { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { @@ -35,7 +35,7 @@ class PlayOpenIDServiceSpec extends PlaySpecification with Mockito { "image" -> "http://axschema.org/media/image/default"), realm = Some("http://localhost:9000")) - val service = new PlayOpenIDService(mock[OpenIdClient], openIDSettings) + val service = new PlayOpenIDService(mock(classOf[OpenIdClient]), openIDSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/CsrfStateItemHandlerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/CsrfStateItemHandlerSpec.scala index 06473612..e7d22297 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/CsrfStateItemHandlerSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/CsrfStateItemHandlerSpec.scala @@ -21,11 +21,13 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialStateI import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialStateItem.ItemStructure import io.github.honeycombcheesecake.play.silhouette.impl.providers.state.CsrfStateItemHandler._ import org.specs2.matcher.JsonMatchers -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.libs.json.Json import play.api.mvc.{ AnyContentAsEmpty, Cookie, Results } import play.api.test.{ FakeRequest, PlaySpecification } +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any +import test.Helper.mockSmart import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -34,11 +36,11 @@ import scala.util.Success /** * Test case for the [[CsrfStateItemHandler]] class. */ -class CsrfStateItemHandlerSpec extends PlaySpecification with Mockito with JsonMatchers { +class CsrfStateItemHandlerSpec extends PlaySpecification with JsonMatchers { "The `item` method" should { "return the CSRF state item" in new Context { - idGenerator.generate returns Future.successful(csrfToken) + when(idGenerator.generate).thenReturn(Future.successful(csrfToken)) await(csrfStateItemHandler.item) must be equalTo csrfStateItem } @@ -50,7 +52,7 @@ class CsrfStateItemHandlerSpec extends PlaySpecification with Mockito with JsonM } "should return `None` if it can't handle the given item" in new Context { - val nonCsrfState = mock[SocialStateItem].smart + val nonCsrfState = mockSmart[SocialStateItem] csrfStateItemHandler.canHandle(nonCsrfState) must beNone } @@ -58,8 +60,8 @@ class CsrfStateItemHandlerSpec extends PlaySpecification with Mockito with JsonM "The `canHandle` method" should { "return false if the give item is for another handler" in new Context { - val nonCsrfItemStructure = mock[ItemStructure].smart - nonCsrfItemStructure.id returns "non-csrf-item" + val nonCsrfItemStructure = mockSmart[ItemStructure] + when(nonCsrfItemStructure.id).thenReturn("non-csrf-item") implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() csrfStateItemHandler.canHandle(nonCsrfItemStructure) must beFalse @@ -107,7 +109,7 @@ class CsrfStateItemHandlerSpec extends PlaySpecification with Mockito with JsonM /** * The ID generator implementation. */ - val idGenerator = mock[IDGenerator].smart + val idGenerator = mockSmart[IDGenerator] /** * The settings. @@ -120,9 +122,9 @@ class CsrfStateItemHandlerSpec extends PlaySpecification with Mockito with JsonM * The signer returns the same value as passed to the methods. This is enough for testing. */ val signer = { - val c = mock[Signer].smart - c.sign(any()) answers { p: Any => p.asInstanceOf[String] } - c.extract(any()) answers { p: Any => Success(p.asInstanceOf[String]) } + val c = mockSmart[Signer] + when(c.sign(any())).thenAnswer(_.getArgument(0).asInstanceOf[String]) + when(c.extract(any())).thenAnswer(p => Success(p.getArgument(0).asInstanceOf[String])) c } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/UserStateItemHandlerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/UserStateItemHandlerSpec.scala index 6521ca43..9cfb7336 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/UserStateItemHandlerSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/state/UserStateItemHandlerSpec.scala @@ -19,18 +19,19 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialStateI import io.github.honeycombcheesecake.play.silhouette.impl.providers.SocialStateItem.ItemStructure import io.github.honeycombcheesecake.play.silhouette.impl.providers.state.UserStateItemHandler._ import org.specs2.matcher.JsonMatchers -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.libs.json.Json import play.api.mvc.AnyContentAsEmpty import play.api.test.{ FakeRequest, PlaySpecification } +import org.mockito.Mockito._ +import test.Helper.mockSmart import scala.concurrent.ExecutionContext.Implicits.global /** * Test case for the [[UserStateItemHandler]] class. */ -class UserStateItemHandlerSpec extends PlaySpecification with Mockito with JsonMatchers { +class UserStateItemHandlerSpec extends PlaySpecification with JsonMatchers { "The `item` method" should { "return the user state item" in new Context { @@ -44,7 +45,7 @@ class UserStateItemHandlerSpec extends PlaySpecification with Mockito with JsonM } "should return `None` if it can't handle the given item" in new Context { - val nonUserState = mock[SocialStateItem].smart + val nonUserState = mockSmart[SocialStateItem] userStateItemHandler.canHandle(nonUserState) must beNone } @@ -52,8 +53,8 @@ class UserStateItemHandlerSpec extends PlaySpecification with Mockito with JsonM "The `canHandle` method" should { "return false if the give item is for another handler" in new Context { - val nonUserItemStructure = mock[ItemStructure].smart - nonUserItemStructure.id returns "non-user-item" + val nonUserItemStructure = mockSmart[ItemStructure] + when(nonUserItemStructure.id).thenReturn("non-user-item") implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() userStateItemHandler.canHandle(nonUserItemStructure) must beFalse diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/services/GravatarServiceSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/services/GravatarServiceSpec.scala index ca5b6a67..11af505a 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/services/GravatarServiceSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/services/GravatarServiceSpec.scala @@ -17,9 +17,11 @@ package io.github.honeycombcheesecake.play.silhouette.impl.services import io.github.honeycombcheesecake.play.silhouette.api.util.{ MockHTTPLayer, MockWSRequest } import io.github.honeycombcheesecake.play.silhouette.impl.services.GravatarService._ -import org.specs2.mock.Mockito import org.specs2.specification.Scope import play.api.test.PlaySpecification +import org.mockito.Mockito._ +import org.mockito.ArgumentMatchers.any +import test.Helper.mock import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -27,7 +29,7 @@ import scala.concurrent.Future /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.services.GravatarService]] class. */ -class GravatarServiceSpec extends PlaySpecification with Mockito { +class GravatarServiceSpec extends PlaySpecification { "The `retrieveURL` method" should { "return None if email is empty" in new Context { @@ -38,9 +40,9 @@ class GravatarServiceSpec extends PlaySpecification with Mockito { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 404 - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(any) returns wsRequest + when(wsResponse.status).thenReturn(404) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(any)).thenReturn(wsRequest) await(service.retrieveURL(email)) should beNone } @@ -49,9 +51,9 @@ class GravatarServiceSpec extends PlaySpecification with Mockito { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 404 - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(any) returns wsRequest + when(wsResponse.status).thenReturn(404) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(any)).thenReturn(wsRequest) await(service.retrieveURL(email)) should beNone } @@ -60,9 +62,9 @@ class GravatarServiceSpec extends PlaySpecification with Mockito { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status throws new RuntimeException("Timeout error") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(any) returns wsRequest + when(wsResponse.status).thenThrow(new RuntimeException("Timeout error")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(any)).thenReturn(wsRequest) await(service.retrieveURL(email)) should beNone } @@ -71,9 +73,9 @@ class GravatarServiceSpec extends PlaySpecification with Mockito { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(any) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(any)).thenReturn(wsRequest) await(service.retrieveURL(email)) should beSome(SecureURL.format(hash, "?d=404")) } @@ -82,10 +84,10 @@ class GravatarServiceSpec extends PlaySpecification with Mockito { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - settings.secure returns false - wsResponse.status returns 200 - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(any) returns wsRequest + when(settings.secure).thenReturn(false) + when(wsResponse.status).thenReturn(200) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(any)).thenReturn(wsRequest) await(service.retrieveURL(email)) should beSome(InsecureURL.format(hash, "?d=404")) } @@ -94,10 +96,10 @@ class GravatarServiceSpec extends PlaySpecification with Mockito { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - settings.params returns Map("d" -> "http://example.com/images/avatar.jpg", "s" -> "400") - wsResponse.status returns 200 - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(any) returns wsRequest + when(settings.params).thenReturn(Map("d" -> "http://example.com/images/avatar.jpg", "s" -> "400")) + when(wsResponse.status).thenReturn(200) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(any)).thenReturn(wsRequest) await(service.retrieveURL(email)) should beSome( SecureURL.format(hash, "?d=http%3A%2F%2Fexample.com%2Fimages%2Favatar.jpg&s=400")) @@ -107,9 +109,9 @@ class GravatarServiceSpec extends PlaySpecification with Mockito { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(any) returns wsRequest + when(wsResponse.status).thenReturn(200) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(any)).thenReturn(wsRequest) await(service.retrieveURL("123test@test.com")) should beSome( SecureURL.format("0d77aed6b4c5857473c9a04c2017f8b8", "?d=404")) @@ -126,7 +128,7 @@ class GravatarServiceSpec extends PlaySpecification with Mockito { */ val httpLayer = { val m = mock[MockHTTPLayer] - m.executionContext returns global + when(m.executionContext).thenReturn(global) m } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/PlayCacheLayerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/PlayCacheLayerSpec.scala index 56510f0f..a5c63671 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/PlayCacheLayerSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/PlayCacheLayerSpec.scala @@ -15,7 +15,7 @@ */ package io.github.honeycombcheesecake.play.silhouette.impl.util -import org.specs2.mock.Mockito +import org.mockito.Mockito._ import org.specs2.specification.Scope import play.api.cache.AsyncCacheApi import play.api.test.PlaySpecification @@ -27,15 +27,15 @@ import scala.concurrent.duration.Duration /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.util.PlayCacheLayer]] class. */ -class PlayCacheLayerSpec extends PlaySpecification with Mockito { +class PlayCacheLayerSpec extends PlaySpecification { "The `find` method" should { "return value from cache" in new Context { - cacheAPI.get[ZonedDateTime]("id") returns Future.successful(Some(value)) + when(cacheAPI.get[ZonedDateTime]("id")).thenReturn(Future.successful(Some(value))) await(layer.find[ZonedDateTime]("id")) should beSome(value) - there was one(cacheAPI).get[ZonedDateTime]("id") + verify(cacheAPI).get[ZonedDateTime]("id") } } @@ -43,7 +43,7 @@ class PlayCacheLayerSpec extends PlaySpecification with Mockito { "save value in cache" in new Context { await(layer.save("id", value)) - there was one(cacheAPI).set("id", value, Duration.Inf) + verify(cacheAPI).set("id", value, Duration.Inf) } } @@ -51,7 +51,7 @@ class PlayCacheLayerSpec extends PlaySpecification with Mockito { "removes value from cache" in new Context { await(layer.remove("id")) must beEqualTo(()) - there was one(cacheAPI).remove("id") + verify(cacheAPI).remove("id") } } @@ -63,7 +63,7 @@ class PlayCacheLayerSpec extends PlaySpecification with Mockito { /** * The cache API. */ - lazy val cacheAPI = mock[AsyncCacheApi] + lazy val cacheAPI = mock(classOf[AsyncCacheApi]) /** * The layer to test. From 2f856a26a8a96b6ceaf5f2c20ff5bde05d81b008 Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Tue, 21 Nov 2023 10:32:33 +0100 Subject: [PATCH 02/30] Activate Scala 3 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 423900cf..6aedbbe5 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,7 @@ import sbt.CrossVersion lazy val repo: String = "https://s01.oss.sonatype.org" lazy val scala213: String = "2.13.12" lazy val scala3: String = "3.3.1" // Ready for cross build, currently not yet supported by play. -lazy val supportedScalaVersions: Seq[String] = Seq(scala213 /*, scala3*/) +lazy val supportedScalaVersions: Seq[String] = Seq(scala213, scala3) Global / evictionErrorLevel := Level.Info From 7a576f54d04e259d991ef02400799f8188e04699 Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Tue, 21 Nov 2023 10:33:39 +0100 Subject: [PATCH 03/30] Some imports needed by Scala 3 --- .../silhouette/impl/authenticators/CookieAuthenticator.scala | 2 ++ .../silhouette/impl/authenticators/SessionAuthenticator.scala | 2 ++ 2 files changed, 4 insertions(+) diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticator.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticator.scala index 0cc93aa0..a3effdd0 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticator.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticator.scala @@ -30,10 +30,12 @@ import io.github.honeycombcheesecake.play.silhouette.api.util._ import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.CookieAuthenticatorService._ import play.api.libs.json.{ Json, OFormat } import play.api.mvc._ +import play.api.mvc.{ Cookie, CookieHeaderEncoding } import play.api.mvc.request.{ Cell, RequestAttrKey } import java.time.ZonedDateTime import scala.concurrent.duration._ +import scala.concurrent.duration.FiniteDuration import scala.concurrent.{ ExecutionContext, Future } import scala.util.{ Failure, Success, Try } diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticator.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticator.scala index 62fddbbe..92cd5084 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticator.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticator.scala @@ -25,10 +25,12 @@ import io.github.honeycombcheesecake.play.silhouette.api.{ Authenticator, Expira import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.SessionAuthenticatorService._ import play.api.libs.json.{ Json, OFormat } import play.api.mvc._ +import play.api.mvc.SessionCookieBaker import play.api.mvc.request.{ Cell, RequestAttrKey } import java.time.ZonedDateTime import scala.concurrent.duration._ +import scala.concurrent.duration.FiniteDuration import scala.concurrent.{ ExecutionContext, Future } import scala.util.{ Failure, Success, Try } From 8f81fbfbbbc911652e3afe8427acab24411e3557 Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Tue, 21 Nov 2023 10:33:53 +0100 Subject: [PATCH 04/30] Import needed by Scala 3 --- .../play/silhouette/impl/providers/OAuth2Provider.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2Provider.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2Provider.scala index 92b54e6a..863abc40 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2Provider.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2Provider.scala @@ -29,6 +29,7 @@ import io.github.honeycombcheesecake.play.silhouette.impl.providers.OAuth2Provid import io.github.honeycombcheesecake.play.silhouette.impl.providers.state.UserStateItemHandler import play.api.libs.functional.syntax._ import play.api.libs.json._ +import play.api.libs.ws.DefaultBodyWritables.writeableOf_urlEncodedForm import play.api.libs.ws.WSResponse import play.api.mvc._ From 71c681354db06c7279658afdaf0a7253c5e83ab2 Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Tue, 21 Nov 2023 10:32:49 +0100 Subject: [PATCH 05/30] WIP Comment some flags Scala 3 does not like --- build.sbt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/build.sbt b/build.sbt index 6aedbbe5..1907c122 100644 --- a/build.sbt +++ b/build.sbt @@ -19,16 +19,16 @@ ThisBuild / organizationName := "honeycomb-cheesecake" ThisBuild / scalaVersion := scala213 ThisBuild / versionScheme := Some("early-semver") ThisBuild / scalacOptions ++= Seq( - "-unchecked", - "-deprecation", + //"-unchecked", + //"-deprecation", "-feature", - "-encoding", "utf8", + //"-encoding", "utf8", "-Xfatal-warnings", - "-Xlint", - "-Xlint:adapted-args", - "-Xlint:inaccessible", - "-Xlint:infer-any", - "-Xlint:nullary-unit" + //"-Xlint", + //"-Xlint:adapted-args", + //"-Xlint:inaccessible", + //"-Xlint:infer-any", + //"-Xlint:nullary-unit" ) ThisBuild / Test / scalacOptions ~= { options: Seq[String] => // Allow dead code in tests (to support using mockito). From 956dbb06c2cd6632dd8cefe36797ace769f76f3e Mon Sep 17 00:00:00 2001 From: Nicolas Deverge Date: Fri, 24 Nov 2023 08:21:45 +0100 Subject: [PATCH 06/30] Using Scala 3 type projection with match types, WIP --- build.sbt | 2 +- .../play/silhouette/test/package.scala | 8 +++--- .../play/silhouette/api/Authenticator.scala | 11 ++++++++ .../play/silhouette/api/Environment.scala | 20 +++++++++++--- .../play/silhouette/api/RequestHandler.scala | 10 +++---- .../api/actions/SecuredAction.scala | 26 +++++++++---------- .../api/actions/UserAwareAction.scala | 14 +++++----- .../api/services/AuthenticatorService.scala | 10 +++---- .../silhouette/api/ErrorHandlerSpec.scala | 2 +- 9 files changed, 63 insertions(+), 40 deletions(-) diff --git a/build.sbt b/build.sbt index 1907c122..a0300759 100644 --- a/build.sbt +++ b/build.sbt @@ -16,7 +16,7 @@ ThisBuild / Test / publishArtifact := false ThisBuild / pomIncludeRepository := { _ => false } ThisBuild / organization := "io.github.honeycomb-cheesecake" ThisBuild / organizationName := "honeycomb-cheesecake" -ThisBuild / scalaVersion := scala213 +ThisBuild / scalaVersion := scala3 ThisBuild / versionScheme := Some("early-semver") ThisBuild / scalacOptions ++= Seq( //"-unchecked", diff --git a/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/package.scala b/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/package.scala index c3ea0424..0c51c197 100644 --- a/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/package.scala +++ b/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/package.scala @@ -43,8 +43,8 @@ package object test { * @param f A fake request instance. * @tparam A The type of the body. */ - implicit class FakeRequestWithAuthenticator[A](f: FakeRequest[A]) { - implicit val request: FakeRequest[A] = f + implicit class FakeRequestWithAuthenticator[F](f: FakeRequest[F]) { + implicit val request: FakeRequest[F] = f /** * Creates a fake request with an embedded authenticator. @@ -54,7 +54,7 @@ package object test { * @tparam E The type of the environment. * @return A fake request. */ - def withAuthenticator[E <: Env](authenticator: E#A)(implicit env: Environment[E]): FakeRequest[A] = { + def withAuthenticator[E <: Env](authenticator: A[E])(implicit env: Environment[E]): FakeRequest[F] = { implicit val ec = env.executionContext val rh = env.authenticatorService.init(authenticator).map(v => env.authenticatorService.embed(v, f)) @@ -69,7 +69,7 @@ package object test { * @tparam E The type of the environment. * @return A fake request. */ - def withAuthenticator[E <: Env](loginInfo: LoginInfo)(implicit env: Environment[E]): FakeRequest[A] = { + def withAuthenticator[E <: Env](loginInfo: LoginInfo)(implicit env: Environment[E]): FakeRequest[F] = { withAuthenticator(FakeAuthenticator[E](loginInfo)) } } diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala index 1d16b870..5ab693ae 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala @@ -54,11 +54,22 @@ trait Authenticator { def isValid: Boolean } + +// match types +type Value[A <: Authenticator] = A match + case Authenticator.AuxV[v] => v // lower case is significant + +type Settings[A <: Authenticator] = A match + case Authenticator.AuxS[s] => s // lower case is significant + /** * The `Authenticator` companion object. */ object Authenticator { + type AuxV[_V] = Authenticator {type Value = _V} + type AuxS[_S] = Authenticator {type Settings = _S} + /** * Some implicits. */ diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala index 0cd8e6e2..9f776d8c 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala @@ -46,6 +46,18 @@ trait Env { type A <: Authenticator } +object Env { + type AuxI[_I <: Identity] = Env {type I = _I} + type AuxA[_A <: Authenticator] = Env {type A = _A} +} + + // match types +type I[E <: Env] <: Identity = E match + case Env.AuxI[i] => i // lower case is significant + +type A[E <: Env] <: Authenticator = E match + case Env.AuxA[a] => a + /** * Provides the components needed to handle a secured request. * @@ -62,14 +74,14 @@ trait Environment[E <: Env] extends ExecutionContextProvider { * * @return The identity service implementation. */ - def identityService: IdentityService[E#I] + def identityService: IdentityService[I[E]] /** * Gets the authenticator service implementation. * * @return The authenticator service implementation. */ - def authenticatorService: AuthenticatorService[E#A] + def authenticatorService: AuthenticatorService[A[E]] /** * Gets the list of request providers. @@ -96,8 +108,8 @@ trait Environment[E <: Env] extends ExecutionContextProvider { */ object Environment { def apply[E <: Env]( - identityServiceImpl: IdentityService[E#I], - authenticatorServiceImpl: AuthenticatorService[E#A], + identityServiceImpl: IdentityService[I[E]], + authenticatorServiceImpl: AuthenticatorService[A[E]], requestProvidersImpl: Seq[RequestProvider], eventBusImpl: EventBus)(implicit ec: ExecutionContext) = new Environment[E] { val identityService = identityServiceImpl diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala index 049d20ab..dd8872ff 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala @@ -57,7 +57,7 @@ trait RequestHandlerBuilder[E <: Env, +R[_]] extends ExecutionContextProvider { /** * The execution context to handle the asynchronous operations. */ - implicit lazy val executionContext: ExecutionContext = environment.executionContext + implicit /*lazy*/ val executionContext: ExecutionContext = environment.executionContext /** * The environment instance to handle the request. @@ -114,7 +114,7 @@ trait RequestHandlerBuilder[E <: Env, +R[_]] extends ExecutionContextProvider { * @tparam T The type of the data included in the handler result. * @return A handler result. */ - protected def handleBlock[T](authenticator: Either[E#A, E#A], block: E#A => Future[HandlerResult[T]])(implicit request: RequestHeader) = { + protected def handleBlock[T](authenticator: Either[A[E], A[E]], block: A[E] => Future[HandlerResult[T]])(implicit request: RequestHeader) = { authenticator match { case Left(a) => handleInitializedAuthenticator(a, block) case Right(a) => handleUninitializedAuthenticator(a, block) @@ -134,7 +134,7 @@ trait RequestHandlerBuilder[E <: Env, +R[_]] extends ExecutionContextProvider { * @return A tuple which consists of (maybe the existing authenticator on the left or a * new authenticator on the right -> maybe the identity). */ - protected def handleAuthentication[B](implicit request: Request[B]): Future[(Option[Either[E#A, E#A]], Option[E#I])] = { + protected def handleAuthentication[B](implicit request: Request[B]): Future[(Option[Either[A[E], A[E]]], Option[I[E]])] = { environment.authenticatorService.retrieve.flatMap { // A valid authenticator was found so we retrieve also the identity case Some(a) if a.isValid => environment.identityService.retrieve(a.loginInfo).map(i => Some(Left(a)) -> i) @@ -164,7 +164,7 @@ trait RequestHandlerBuilder[E <: Env, +R[_]] extends ExecutionContextProvider { * @tparam T The type of the data included in the handler result. * @return A handler result. */ - private def handleInitializedAuthenticator[T](authenticator: E#A, block: E#A => Future[HandlerResult[T]])(implicit request: RequestHeader) = { + private def handleInitializedAuthenticator[T](authenticator: A[E], block: A[E] => Future[HandlerResult[T]])(implicit request: RequestHeader) = { val auth = environment.authenticatorService.touch(authenticator) block(auth.fold(identity, identity)).flatMap { case hr @ HandlerResult(pr: AuthenticatorResult, _) => Future.successful(hr) @@ -189,7 +189,7 @@ trait RequestHandlerBuilder[E <: Env, +R[_]] extends ExecutionContextProvider { * @tparam T The type of the data included in the handler result. * @return A handler result. */ - private def handleUninitializedAuthenticator[T](authenticator: E#A, block: E#A => Future[HandlerResult[T]])(implicit request: RequestHeader) = { + private def handleUninitializedAuthenticator[T](authenticator: A[E], block: A[E] => Future[HandlerResult[T]])(implicit request: RequestHeader) = { block(authenticator).flatMap { case hr @ HandlerResult(pr: AuthenticatorResult, _) => Future.successful(hr) case hr @ HandlerResult(pr, _) => diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala index 3a8f1792..7010ecfa 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala @@ -20,14 +20,14 @@ package io.github.honeycombcheesecake.play.silhouette.api.actions import javax.inject.Inject - import io.github.honeycombcheesecake.play.silhouette.api._ import play.api.i18n.MessagesApi import play.api.inject.Module import play.api.mvc._ -import play.api.{ Configuration, Environment => PlayEnv } +import play.api.{Configuration, Environment => PlayEnv} -import scala.concurrent.{ ExecutionContext, Future } +import scala.concurrent.{ExecutionContext, Future} +import scala.reflect.ClassTag /** * A request header that only allows access if an identity is authenticated and authorized. @@ -38,12 +38,12 @@ trait SecuredRequestHeader[E <: Env] extends RequestHeader { /** * @return The identity implementation. */ - def identity: E#I + def identity: I[E] /** * @return The authenticator implementation. */ - def authenticator: E#A + def authenticator: A[E] } /** @@ -64,7 +64,7 @@ object SecuredRequest { * @tparam E The type of the environment. * @tparam B The type of the request body. */ - def apply[E <: Env, B](identity: E#I, authenticator: E#A, request: Request[B]): SecuredRequest[E, B] = { + def apply[E <: Env, B](identity: I[E], authenticator: A[E], request: Request[B]): SecuredRequest[E, B] = { new DefaultSecuredRequest(identity, authenticator, request) } @@ -75,7 +75,7 @@ object SecuredRequest { * @tparam E The type of the environment. * @tparam B The type of the request body. */ - def unapply[E <: Env, B](securedRequest: SecuredRequest[E, B]): Option[(E#I, E#A, Request[B])] = { + def unapply[E <: Env, B](securedRequest: SecuredRequest[E, B]): Option[(I[E], A[E], Request[B])] = { securedRequest match { case dsr: DefaultSecuredRequest[E, B] => Some((dsr.identity, dsr.authenticator, dsr.request)) @@ -86,8 +86,8 @@ object SecuredRequest { } class DefaultSecuredRequest[E <: Env, B]( - val identity: E#I, - val authenticator: E#A, + val identity: I[E], + val authenticator: A[E], val request: Request[B]) extends WrappedRequest(request) with SecuredRequest[E, B] /** @@ -101,7 +101,7 @@ class DefaultSecuredRequest[E <: Env, B]( final case class SecuredRequestHandlerBuilder[E <: Env]( environment: Environment[E], errorHandler: SecuredErrorHandler, - authorization: Option[Authorization[E#I, E#A]]) + authorization: Option[Authorization[I[E], A[E]]]) extends RequestHandlerBuilder[E, ({ type R[B] = SecuredRequest[E, B] })#R] { /** @@ -119,7 +119,7 @@ final case class SecuredRequestHandlerBuilder[E <: Env]( * @param specifiedAuthorization An authorization object that checks if the user is authorized to invoke the action. * @return A secured action handler builder with an authorization in place. */ - def apply(specifiedAuthorization: Authorization[E#I, E#A]): SecuredRequestHandlerBuilder[E] = + def apply(specifiedAuthorization: Authorization[I[E], A[E]]): SecuredRequestHandlerBuilder[E] = SecuredRequestHandlerBuilder[E](environment, errorHandler, Some(specifiedAuthorization)) /** @@ -165,7 +165,7 @@ final case class SecuredRequestHandlerBuilder[E <: Env]( * @tparam B The type of the request body. * @return The authentication result with the additional authorization status. */ - private def withAuthorization[B](result: Future[(Option[Either[E#A, E#A]], Option[E#I])])(implicit request: Request[B]) = { + private def withAuthorization[B](result: Future[(Option[Either[A[E], A[E]]], Option[I[E]])])(implicit request: Request[B]) = { result.flatMap { case (Some(a), Some(i)) => authorization.map(_.isAuthorized(i, a.extract)).getOrElse(Future.successful(true)).map(b => (Some(a), Some(i), Some(b))) @@ -247,7 +247,7 @@ final case class SecuredActionBuilder[E <: Env, P]( * @param authorization An authorization object that checks if the user is authorized to invoke the action. * @return A secured action builder. */ - def apply(authorization: Authorization[E#I, E#A]): SecuredActionBuilder[E, P] = SecuredActionBuilder[E, P](requestHandler(authorization), parser) + def apply(authorization: Authorization[I[E], A[E]]): SecuredActionBuilder[E, P] = SecuredActionBuilder[E, P](requestHandler(authorization), parser) /** * Invokes the block. diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala index 40678961..01481001 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala @@ -37,12 +37,12 @@ trait UserAwareRequestHeader[E <: Env] extends RequestHeader { /** * @return Some identity implementation if authentication was successful, None otherwise. */ - def identity: Option[E#I] + def identity: Option[I[E]] /** * @return Some authenticator implementation if authentication was successful, None otherwise. */ - def authenticator: Option[E#A] + def authenticator: Option[A[E]] } trait UserAwareRequest[E <: Env, +B] extends Request[B] with UserAwareRequestHeader[E] @@ -59,8 +59,8 @@ object UserAwareRequest { * @tparam B The type of the request body. */ def apply[E <: Env, B]( - identity: Option[E#I], - authenticator: Option[E#A], + identity: Option[I[E]], + authenticator: Option[A[E]], request: Request[B]): UserAwareRequest[E, B] = { new DefaultUserAwareRequest(identity, authenticator, request) } @@ -72,7 +72,7 @@ object UserAwareRequest { * @tparam E The type of the environment. * @tparam B The type of the request body. */ - def unapply[E <: Env, B](userAwareRequest: UserAwareRequest[E, B]): Option[(Option[E#I], Option[E#A], Request[B])] = { + def unapply[E <: Env, B](userAwareRequest: UserAwareRequest[E, B]): Option[(Option[I[E]], Option[A[E]], Request[B])] = { userAwareRequest match { case duar: DefaultUserAwareRequest[E, B] => Some((duar.identity, duar.authenticator, duar.request)) @@ -83,8 +83,8 @@ object UserAwareRequest { } class DefaultUserAwareRequest[E <: Env, B]( - val identity: Option[E#I], - val authenticator: Option[E#A], + val identity: Option[I[E]], + val authenticator: Option[A[E]], val request: Request[B]) extends WrappedRequest(request) with UserAwareRequest[E, B] /** diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala index efc17d93..8ab07dcb 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala @@ -20,7 +20,7 @@ package io.github.honeycombcheesecake.play.silhouette.api.services import io.github.honeycombcheesecake.play.silhouette.api.util.{ ExtractableRequest, ExecutionContextProvider } -import io.github.honeycombcheesecake.play.silhouette.api.{ Authenticator, LoginInfo } +import io.github.honeycombcheesecake.play.silhouette.api.{ Authenticator, LoginInfo, Value } import play.api.http.HttpEntity import play.api.libs.typedmap.TypedMap import play.api.mvc._ @@ -115,7 +115,7 @@ trait AuthenticatorService[T <: Authenticator] extends ExecutionContextProvider * @param request The request header. * @return The serialized authenticator value. */ - def init(authenticator: T)(implicit request: RequestHeader): Future[T#Value] + def init(authenticator: T)(implicit request: RequestHeader): Future[Value[T]] /** * Embeds authenticator specific artifacts into the response. @@ -125,7 +125,7 @@ trait AuthenticatorService[T <: Authenticator] extends ExecutionContextProvider * @param request The request header. * @return The manipulated result. */ - def embed(value: T#Value, result: Result)(implicit request: RequestHeader): Future[AuthenticatorResult] + def embed(value: Value[T], result: Result)(implicit request: RequestHeader): Future[AuthenticatorResult] /** * Embeds authenticator specific artifacts into the request. @@ -141,7 +141,7 @@ trait AuthenticatorService[T <: Authenticator] extends ExecutionContextProvider * @param request The request header. * @return The manipulated request header. */ - def embed(value: T#Value, request: RequestHeader): RequestHeader + def embed(value: Value[T], request: RequestHeader): RequestHeader /** * Touches an authenticator. @@ -182,7 +182,7 @@ trait AuthenticatorService[T <: Authenticator] extends ExecutionContextProvider * @param request The request header. * @return The serialized expression of the authenticator. */ - def renew(authenticator: T)(implicit request: RequestHeader): Future[T#Value] + def renew(authenticator: T)(implicit request: RequestHeader): Future[Value[T]] /** * Renews the expiration of an authenticator. diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala index 4b577b95..8b14bb63 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala @@ -36,7 +36,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = HTML, expectedResponseFragment = "", expectedMessage = "silhouette.not.authenticated", - f = { r: RequestHeader => notAuthenticated.onNotAuthenticated(r) }) + f = { (r: RequestHeader) => notAuthenticated.onNotAuthenticated(r) }) } "return a JSON response for a JSON request" in new WithApplication with Context { From 7f1e8d771ba36a75fcd1b5c1f5b756b2766c3787 Mon Sep 17 00:00:00 2001 From: Nicolas Deverge Date: Fri, 24 Nov 2023 09:51:43 +0100 Subject: [PATCH 07/30] Temporary removal of Scala 2.13 to focus on Scala 3 migration. --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index a0300759..cf76dd73 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,7 @@ import sbt.CrossVersion lazy val repo: String = "https://s01.oss.sonatype.org" lazy val scala213: String = "2.13.12" lazy val scala3: String = "3.3.1" // Ready for cross build, currently not yet supported by play. -lazy val supportedScalaVersions: Seq[String] = Seq(scala213, scala3) +lazy val supportedScalaVersions: Seq[String] = Seq(/*scala213,*/ scala3) Global / evictionErrorLevel := Level.Info From a899acd5d9e915607ab882dd4122d7a02df1ba15 Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Fri, 24 Nov 2023 13:56:46 +0100 Subject: [PATCH 08/30] Silence warnings --- .../play/silhouette/test/Fakes.scala | 10 +++++----- .../play/silhouette/api/actions/SecuredAction.scala | 2 ++ .../play/silhouette/api/actions/UserAwareAction.scala | 2 ++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala b/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala index ca49eb75..cd4053f9 100644 --- a/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala +++ b/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala @@ -210,7 +210,7 @@ object FakeAuthenticator { * @tparam E The type of the environment, * @return A authenticator instance. */ - def apply[E <: Env](loginInfo: LoginInfo)(implicit env: Environment[E], requestHeader: RequestHeader): E#A = { + def apply[E <: Env](loginInfo: LoginInfo)(implicit env: Environment[E], requestHeader: RequestHeader): A[E] = { env.authenticatorService.create(loginInfo) } } @@ -226,20 +226,20 @@ object FakeAuthenticator { * @tparam E The type of the environment. */ final case class FakeEnvironment[E <: Env]( - identities: Seq[(LoginInfo, E#I)], + identities: Seq[(LoginInfo, I[E])], requestProviders: Seq[RequestProvider] = Seq.empty, eventBus: EventBus = EventBus())( implicit val executionContext: ExecutionContext, - tt: TypeTag[E#A]) extends Environment[E] { + tt: TypeTag[A[E]]) extends Environment[E] { /** * The identity service implementation. */ - val identityService: IdentityService[E#I] = new FakeIdentityService[E#I](identities: _*) + val identityService: IdentityService[I[E]] = new FakeIdentityService[I[E]](identities: _*) /** * The authenticator service implementation. */ - val authenticatorService: AuthenticatorService[E#A] = FakeAuthenticatorService[E#A]() + val authenticatorService: AuthenticatorService[A[E]] = FakeAuthenticatorService[A[E]]() } diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala index 7010ecfa..4c264759 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala @@ -26,6 +26,7 @@ import play.api.inject.Module import play.api.mvc._ import play.api.{Configuration, Environment => PlayEnv} +import scala.annotation.nowarn import scala.concurrent.{ExecutionContext, Future} import scala.reflect.ClassTag @@ -75,6 +76,7 @@ object SecuredRequest { * @tparam E The type of the environment. * @tparam B The type of the request body. */ + @nowarn def unapply[E <: Env, B](securedRequest: SecuredRequest[E, B]): Option[(I[E], A[E], Request[B])] = { securedRequest match { case dsr: DefaultSecuredRequest[E, B] => diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala index 01481001..0d4783d7 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala @@ -26,6 +26,7 @@ import play.api.{ Configuration, Environment => PlayEnv } import play.api.inject.Module import play.api.mvc._ +import scala.annotation.nowarn import scala.concurrent.{ ExecutionContext, Future } /** @@ -72,6 +73,7 @@ object UserAwareRequest { * @tparam E The type of the environment. * @tparam B The type of the request body. */ + @nowarn def unapply[E <: Env, B](userAwareRequest: UserAwareRequest[E, B]): Option[(Option[I[E]], Option[A[E]], Request[B])] = { userAwareRequest match { case duar: DefaultUserAwareRequest[E, B] => From b25a3882adf980e4ced9e3cfb984dd79f80413a1 Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Fri, 24 Nov 2023 14:52:42 +0100 Subject: [PATCH 09/30] Use izumi reflect as workaround scala reflect runtime --- build.sbt | 1 + project/Dependencies.scala | 1 + .../play/silhouette/test/Fakes.scala | 18 +++++++++--------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/build.sbt b/build.sbt index cf76dd73..aa6a3994 100644 --- a/build.sbt +++ b/build.sbt @@ -227,6 +227,7 @@ lazy val silhouetteTestkit = (project in file("silhouette-testkit")) libraryDependencies ++= Library.updates ++ Seq( Library.Play.test, + Library.izumiReflect, Library.Play.specs2 % Test, Library.Specs2.matcherExtra % Test, Library.mockito % Test, diff --git a/project/Dependencies.scala b/project/Dependencies.scala index e0b7a884..3a898056 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -51,5 +51,6 @@ object Dependencies { val casClientSupportSAML = "org.jasig.cas.client" % "cas-client-support-saml" % "3.6.4" val apacheCommonLang = "org.apache.commons" % "commons-lang3" % "3.13.0" val googleAuth = "com.warrenstrange" % "googleauth" % "1.5.0" + val izumiReflect = "dev.zio" %% "izumi-reflect" % "2.3.8" } } diff --git a/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala b/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala index cd4053f9..09000612 100644 --- a/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala +++ b/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala @@ -23,12 +23,12 @@ import io.github.honeycombcheesecake.play.silhouette.api.services.{ Authenticato import io.github.honeycombcheesecake.play.silhouette.api.util.Clock import io.github.honeycombcheesecake.play.silhouette.impl.authenticators._ import io.github.honeycombcheesecake.play.silhouette.impl.util.{ DefaultFingerprintGenerator, SecureRandomIDGenerator } +import izumi.reflect.Tag import play.api.mvc.{ DefaultCookieHeaderEncoding, DefaultSessionCookieBaker, RequestHeader } import scala.collection.mutable import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.{ ExecutionContext, Future } -import scala.reflect.runtime.universe._ import scala.util import scala.util.Success @@ -175,13 +175,13 @@ object FakeAuthenticatorService { * @tparam T The type of the authenticator. * @return A fully configured authenticator instance. */ - def apply[T <: Authenticator: TypeTag](): AuthenticatorService[T] = { - (typeOf[T] match { - case t if t <:< typeOf[SessionAuthenticator] => FakeSessionAuthenticatorService - case t if t <:< typeOf[CookieAuthenticator] => FakeCookieAuthenticatorService - case t if t <:< typeOf[BearerTokenAuthenticator] => FakeBearerTokenAuthenticatorService - case t if t <:< typeOf[JWTAuthenticator] => FakeJWTAuthenticatorService - case t if t <:< typeOf[DummyAuthenticator] => FakeDummyAuthenticatorService + def apply[T <: Authenticator: Tag](): AuthenticatorService[T] = { + (Tag[T] match { + case t if t <:< Tag[SessionAuthenticator] => FakeSessionAuthenticatorService + case t if t <:< Tag[CookieAuthenticator] => FakeCookieAuthenticatorService + case t if t <:< Tag[BearerTokenAuthenticator] => FakeBearerTokenAuthenticatorService + case t if t <:< Tag[JWTAuthenticator] => FakeJWTAuthenticatorService + case t if t <:< Tag[DummyAuthenticator] => FakeDummyAuthenticatorService case _ => throw new Exception("Invalid type specified.") }).asInstanceOf[AuthenticatorService[T]] } @@ -231,7 +231,7 @@ final case class FakeEnvironment[E <: Env]( eventBus: EventBus = EventBus())( implicit val executionContext: ExecutionContext, - tt: TypeTag[A[E]]) extends Environment[E] { + tt: Tag[A[E]]) extends Environment[E] { /** * The identity service implementation. From 882b4bc603c9592a0d466cc2b9e0f776eee84ff9 Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Fri, 24 Nov 2023 15:57:58 +0100 Subject: [PATCH 10/30] Start fixing some compile errors --- .../silhouette/api/ErrorHandlerSpec.scala | 24 ++--- .../api/actions/UserAwareActionSpec.scala | 4 +- .../custom/FacebookProviderSpec.scala | 91 ++++++++++--------- 3 files changed, 60 insertions(+), 59 deletions(-) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala index 8b14bb63..c773ec22 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala @@ -36,7 +36,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = HTML, expectedResponseFragment = "", expectedMessage = "silhouette.not.authenticated", - f = { (r: RequestHeader) => notAuthenticated.onNotAuthenticated(r) }) + f = { notAuthenticated.onNotAuthenticated(_: RequestHeader) }) } "return a JSON response for a JSON request" in new WithApplication with Context { @@ -46,7 +46,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = JSON, expectedResponseFragment = "\"success\":false", expectedMessage = "silhouette.not.authenticated", - f = { r: RequestHeader => notAuthenticated.onNotAuthenticated(r) }) + f = { notAuthenticated.onNotAuthenticated(_: RequestHeader) }) } "return a XML response for a XML request" in new WithApplication with Context { @@ -56,7 +56,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = XML, expectedResponseFragment = "false", expectedMessage = "silhouette.not.authenticated", - f = { r: RequestHeader => notAuthenticated.onNotAuthenticated(r) }) + f = { notAuthenticated.onNotAuthenticated(_: RequestHeader) }) } "return a plain text response for a plain text request" in new WithApplication with Context { @@ -66,7 +66,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = TEXT, expectedResponseFragment = messagesApi("silhouette.not.authenticated"), expectedMessage = "silhouette.not.authenticated", - f = { r: RequestHeader => notAuthenticated.onNotAuthenticated(r) }) + f = { notAuthenticated.onNotAuthenticated(_: RequestHeader) }) } "return a plain text response for other requests" in new WithApplication with Context { @@ -76,7 +76,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = TEXT, expectedResponseFragment = messagesApi("silhouette.not.authenticated"), expectedMessage = "silhouette.not.authenticated", - f = { r: RequestHeader => notAuthenticated.onNotAuthenticated(r) }) + f = { notAuthenticated.onNotAuthenticated(_: RequestHeader) }) } "return an HTML response for a request without an Accept header" in new WithApplication with Context { @@ -86,7 +86,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = HTML, expectedResponseFragment = messagesApi("silhouette.not.authenticated"), expectedMessage = "silhouette.not.authenticated", - f = { r: RequestHeader => notAuthenticated.onNotAuthenticated(r) }) + f = { notAuthenticated.onNotAuthenticated(_: RequestHeader) }) } } @@ -98,7 +98,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = HTML, expectedResponseFragment = "", expectedMessage = "silhouette.not.authorized", - f = { r: RequestHeader => notAuthorized.onNotAuthorized(r) }) + f = { notAuthorized.onNotAuthorized(_: RequestHeader) }) } "return a JSON response for a JSON request" in new WithApplication with Context { @@ -108,7 +108,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = JSON, expectedResponseFragment = "\"success\":false", expectedMessage = "silhouette.not.authorized", - f = { r: RequestHeader => notAuthorized.onNotAuthorized(r) }) + f = { notAuthorized.onNotAuthorized(_: RequestHeader) }) } "return a XML response for a XML request" in new WithApplication with Context { @@ -118,7 +118,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = XML, expectedResponseFragment = "false", expectedMessage = "silhouette.not.authorized", - f = { r: RequestHeader => notAuthorized.onNotAuthorized(r) }) + f = { notAuthorized.onNotAuthorized(_: RequestHeader) }) } "return a plain text response for a plain text request" in new WithApplication with Context { @@ -128,7 +128,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = TEXT, expectedResponseFragment = messagesApi("silhouette.not.authorized"), expectedMessage = "silhouette.not.authorized", - f = { r: RequestHeader => notAuthorized.onNotAuthorized(r) }) + f = { notAuthorized.onNotAuthorized(_: RequestHeader) }) } "return a plain text response for other requests" in new WithApplication with Context { @@ -138,7 +138,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = TEXT, expectedResponseFragment = messagesApi("silhouette.not.authorized"), expectedMessage = "silhouette.not.authorized", - f = { r: RequestHeader => notAuthorized.onNotAuthorized(r) }) + f = { notAuthorized.onNotAuthorized(_: RequestHeader) }) } "return an HTML response for a request without an Accept header" in new WithApplication with Context { @@ -148,7 +148,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedContentType = HTML, expectedResponseFragment = messagesApi("silhouette.not.authorized"), expectedMessage = "silhouette.not.authorized", - f = { r: RequestHeader => notAuthorized.onNotAuthorized(r) }) + f = { notAuthorized.onNotAuthorized(_: RequestHeader) }) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala index 1140f738..08236685 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala @@ -110,7 +110,7 @@ class UserAwareActionSpec extends PlaySpecification with JsonMatchers with NoLan when(basicAuthRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) - when(env.authenticatorService.init(any())(any())).thenAnswer { p: Any => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) } + when(env.authenticatorService.init(any())(any())).thenAnswer { p => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) } when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } @@ -163,7 +163,7 @@ class UserAwareActionSpec extends PlaySpecification with JsonMatchers with NoLan when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) - when(env.authenticatorService.init(any())(any())).thenAnswer { p: Any => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) } + when(env.authenticatorService.init(any())(any())).thenAnswer { p => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) } when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala index 3ec4d809..56c4e9b0 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala @@ -27,6 +27,7 @@ import play.api.libs.json.{ JsValue, Json } import play.api.mvc.AnyContentAsEmpty import play.api.test.{ FakeRequest, WithApplication } import test.Helper +import org.mockito.Mockito.* import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.{ ExecutionContext, Future } @@ -34,7 +35,7 @@ import scala.concurrent.{ ExecutionContext, Future } /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth2.FacebookProvider]] class which uses a custom social profile. */ -class FacebookProviderSpec extends OAuth2ProviderSpec with org.specs2.mock.Mockito { +class FacebookProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { @@ -48,16 +49,16 @@ class FacebookProviderSpec extends OAuth2ProviderSpec with org.specs2.mock.Mocki "The `authenticate` method" should { "fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] + val wsRequest = mock(classOf[MockWSRequest]) + val wsResponse = mock(classOf[MockWSRequest#Response]) implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 401 - wsResponse.body returns "Unauthorized" - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) @@ -65,16 +66,16 @@ class FacebookProviderSpec extends OAuth2ProviderSpec with org.specs2.mock.Mocki } "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] + val wsRequest = mock(classOf[MockWSRequest]) + val wsResponse = mock(classOf[MockWSRequest#Response]) implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns Json.obj() - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any))).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL))).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]))).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext]))).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) @@ -82,16 +83,16 @@ class FacebookProviderSpec extends OAuth2ProviderSpec with org.specs2.mock.Mocki } "return the auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] + val wsRequest = mock(classOf[MockWSRequest]) + val wsResponse = mock(classOf[MockWSRequest#Response]) implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - wsResponse.status returns 200 - wsResponse.json returns oAuthInfo - wsRequest.withHttpHeaders(any) returns wsRequest - wsRequest.post[Map[String, Seq[String]]](any)(any) returns Future.successful(wsResponse) - httpLayer.url(oAuthSettings.accessTokenURL) returns wsRequest - stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]) returns Future.successful(state) - stateProvider.state(any[ExecutionContext]) returns Future.successful(state) + when(wsResponse.status)).thenReturn(200) + when(wsResponse.json)).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any))).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any))).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL))).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]))).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext]))).thenReturn(Future.successful(state)) authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) } @@ -99,12 +100,12 @@ class FacebookProviderSpec extends OAuth2ProviderSpec with org.specs2.mock.Mocki "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 400 - wsResponse.json returns Helper.loadJson("providers/custom/facebook.error.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + val wsRequest = mock(classOf[MockWSRequest]) + val wsResponse = mock(classOf[MockWSRequest#Response]) + when(wsResponse.status).thenReturn(400) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/custom/facebook.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token")))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -116,12 +117,12 @@ class FacebookProviderSpec extends OAuth2ProviderSpec with org.specs2.mock.Mocki } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 500 + val wsRequest = mock(classOf[MockWSRequest]) + val wsResponse = mock(classOf[MockWSRequest#Response]) + when(wsResponse.status)).thenReturn(500) wsResponse.json throws new RuntimeException("") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + when(wsRequest.get())).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) @@ -129,12 +130,12 @@ class FacebookProviderSpec extends OAuth2ProviderSpec with org.specs2.mock.Mocki } "return the social profile" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - wsResponse.status returns 200 - wsResponse.json returns Helper.loadJson("providers/custom/facebook.success.json") - wsRequest.get() returns Future.successful(wsResponse) - httpLayer.url(API.format("my.access.token")) returns wsRequest + val wsRequest = mock(classOf[MockWSRequest]) + val wsResponse = mock(classOf[MockWSRequest#Response]) + when(wsResponse.status)).thenReturn(200) + when(wsResponse.json)).thenReturn(Helper.loadJson("providers/custom/facebook.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => p must be equalTo CustomSocialProfile( From 0502c0472430b7daef587e857c74c94172129521 Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Mon, 27 Nov 2023 10:50:51 +0100 Subject: [PATCH 11/30] Fixes --- .../daos/InMemoryAuthInfoDAOSpec.scala | 3 +- .../DelegableAuthInfoRepositorySpec.scala | 3 +- .../play/silhouette/api/EventBusSpec.scala | 3 +- .../api/actions/SecuredActionSpec.scala | 3 +- .../api/actions/UnsecuredActionSpec.scala | 3 +- .../api/actions/UserAwareActionSpec.scala | 3 +- .../BearerTokenAuthenticatorSpec.scala | 3 +- .../CookieAuthenticatorSpec.scala | 3 +- .../authenticators/JWTAuthenticatorSpec.scala | 3 +- .../SessionAuthenticatorSpec.scala | 3 +- .../custom/FacebookProviderSpec.scala | 62 ++++++------------- .../oauth1/secrets/CookieSecretSpec.scala | 3 +- 12 files changed, 29 insertions(+), 66 deletions(-) diff --git a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala index 9e2ed65c..a2a7025c 100644 --- a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala +++ b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala @@ -18,7 +18,6 @@ package io.github.honeycombcheesecake.play.silhouette.persistence.daos import io.github.honeycombcheesecake.play.silhouette.api.{ AuthInfo, LoginInfo } import io.github.honeycombcheesecake.play.silhouette.test.WaitPatience import org.specs2.concurrent.ExecutionEnv -import org.specs2.control.NoLanguageFeatures import org.specs2.mutable.Specification import org.specs2.specification.Scope @@ -29,7 +28,7 @@ import scala.language.postfixOps /** * Test case for the [[InMemoryAuthInfoDAO]] trait. */ -class InMemoryAuthInfoDAOSpec(implicit ev: ExecutionEnv) extends Specification with NoLanguageFeatures with WaitPatience { +class InMemoryAuthInfoDAOSpec(implicit ev: ExecutionEnv) extends Specification with WaitPatience { "The `find` method" should { "find an OAuth1 info for the given login info" in new Context { diff --git a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala index e8c75cbe..83fe6f96 100644 --- a/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala +++ b/silhouette-persistence/src/test/scala/io/github/honeycombcheesecake/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala @@ -26,7 +26,6 @@ import io.github.honeycombcheesecake.play.silhouette.persistence.repositories.De import io.github.honeycombcheesecake.play.silhouette.test.WaitPatience import net.codingwell.scalaguice.ScalaModule import org.specs2.concurrent.ExecutionEnv -import org.specs2.control.NoLanguageFeatures import org.specs2.mutable.Specification import org.specs2.specification.Scope import org.mockito.Mockito._ @@ -39,7 +38,7 @@ import scala.language.postfixOps * Test case for the [[DelegableAuthInfoRepository]] trait. */ class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv) - extends Specification with NoLanguageFeatures with WaitPatience { + extends Specification with WaitPatience { "The `find` method" should { "delegate the PasswordInfo to the correct DAO" in new Context { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/EventBusSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/EventBusSpec.scala index e7109774..491f9bca 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/EventBusSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/EventBusSpec.scala @@ -17,7 +17,6 @@ package io.github.honeycombcheesecake.play.silhouette.api import akka.actor.{ Actor, ActorSystem, Props } import akka.testkit.TestProbe -import org.specs2.control.NoLanguageFeatures import org.specs2.specification.Scope import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } @@ -27,7 +26,7 @@ import scala.language.postfixOps /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.api.EventBus]] class. */ -class EventBusSpec extends PlaySpecification with NoLanguageFeatures { +class EventBusSpec extends PlaySpecification { "The event bus" should { "handle an subclass event" in new WithApplication with Context { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala index 29888a75..7ab2a36c 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala @@ -24,7 +24,6 @@ import io.github.honeycombcheesecake.play.silhouette.api.actions.SecuredActionSp import io.github.honeycombcheesecake.play.silhouette.api.exceptions.{ NotAuthenticatedException, NotAuthorizedException } import io.github.honeycombcheesecake.play.silhouette.api.services.{ AuthenticatorResult, AuthenticatorService, IdentityService } import net.codingwell.scalaguice.ScalaModule -import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.JsonMatchers import org.specs2.specification.Scope import play.api.inject.bind @@ -46,7 +45,7 @@ import scala.reflect.ClassTag /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.api.actions.SecuredActionSpec]]. */ -class SecuredActionSpec extends PlaySpecification with JsonMatchers with NoLanguageFeatures { +class SecuredActionSpec extends PlaySpecification with JsonMatchers { "The `SecuredAction` action" should { "restrict access if no valid authenticator can be retrieved" in new InjectorContext { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala index ee73b1c4..ec872c96 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala @@ -24,7 +24,6 @@ import io.github.honeycombcheesecake.play.silhouette.api.actions.UnsecuredAction import io.github.honeycombcheesecake.play.silhouette.api.exceptions.NotAuthorizedException import io.github.honeycombcheesecake.play.silhouette.api.services.{ AuthenticatorResult, AuthenticatorService, IdentityService } import net.codingwell.scalaguice.ScalaModule -import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.JsonMatchers import org.specs2.specification.Scope import play.api.inject.bind @@ -43,7 +42,7 @@ import scala.reflect.ClassTag /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.api.actions.UnsecuredActionSpec]]. */ -class UnsecuredActionSpec extends PlaySpecification with JsonMatchers with NoLanguageFeatures { +class UnsecuredActionSpec extends PlaySpecification with JsonMatchers { "The `UnsecuredAction` action" should { "grant access if no valid authenticator can be retrieved" in new InjectorContext { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala index 08236685..e5ebbe21 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala @@ -21,7 +21,6 @@ import io.github.honeycombcheesecake.play.silhouette.api._ import io.github.honeycombcheesecake.play.silhouette.api.actions.UserAwareActionSpec._ import io.github.honeycombcheesecake.play.silhouette.api.services.{ AuthenticatorResult, AuthenticatorService, IdentityService } import net.codingwell.scalaguice.ScalaModule -import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.JsonMatchers import org.specs2.specification.Scope import play.api.i18n.{ Lang, Langs, MessagesApi } @@ -39,7 +38,7 @@ import scala.concurrent.Future /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.api.actions.UserAwareAction]]. */ -class UserAwareActionSpec extends PlaySpecification with JsonMatchers with NoLanguageFeatures { +class UserAwareActionSpec extends PlaySpecification with JsonMatchers { "The `UserAwareAction` action" should { "invoke action without identity and authenticator if no authenticator could be found" in new InjectorContext { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala index 6789a9ad..c2b89853 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala @@ -22,7 +22,6 @@ import io.github.honeycombcheesecake.play.silhouette.api.repositories.Authentica import io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorService._ import io.github.honeycombcheesecake.play.silhouette.api.util.{ RequestPart, Clock, IDGenerator } import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.BearerTokenAuthenticatorService._ -import org.specs2.control.NoLanguageFeatures import org.specs2.specification.Scope import play.api.mvc.{ Results, AnyContentAsEmpty } import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } @@ -39,7 +38,7 @@ import scala.language.postfixOps /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.BearerTokenAuthenticator]]. */ -class BearerTokenAuthenticatorSpec extends PlaySpecification with NoLanguageFeatures { +class BearerTokenAuthenticatorSpec extends PlaySpecification { "The `isValid` method of the authenticator" should { "return false if the authenticator is expired" in new Context { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala index 1d8f47d1..697dc501 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala @@ -26,7 +26,6 @@ import io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorS import io.github.honeycombcheesecake.play.silhouette.api.util.{ Clock, FingerprintGenerator, IDGenerator } import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.CookieAuthenticator._ import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.CookieAuthenticatorService._ -import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.MatchResult import org.specs2.specification.Scope import play.api.mvc.{ AnyContentAsEmpty, Cookie, DefaultCookieHeaderEncoding, Results } @@ -45,7 +44,7 @@ import scala.util.{ Failure, Success } /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.CookieAuthenticator]]. */ -class CookieAuthenticatorSpec extends PlaySpecification with NoLanguageFeatures { +class CookieAuthenticatorSpec extends PlaySpecification { "The `isValid` method of the authenticator" should { "return false if the authenticator is expired" in new Context { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala index caffac07..649f447c 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala @@ -25,7 +25,6 @@ import io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorS import io.github.honeycombcheesecake.play.silhouette.api.util.{ Clock, IDGenerator, RequestPart } import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.JWTAuthenticator._ import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.JWTAuthenticatorService._ -import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.JsonMatchers import org.specs2.specification.Scope import play.api.libs.json.{ JsNull, JsObject, Json } @@ -45,7 +44,7 @@ import scala.language.postfixOps /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.JWTAuthenticator]]. */ -class JWTAuthenticatorSpec extends PlaySpecification with JsonMatchers with NoLanguageFeatures { +class JWTAuthenticatorSpec extends PlaySpecification with JsonMatchers { "The `isValid` method of the authenticator" should { "return false if the authenticator is expired" in new Context { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala index 8326de3e..50d6a8b6 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala @@ -25,7 +25,6 @@ import io.github.honeycombcheesecake.play.silhouette.api.util.{ Clock, Fingerpri import io.github.honeycombcheesecake.play.silhouette.api.{ Authenticator, LoginInfo } import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.SessionAuthenticator._ import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.SessionAuthenticatorService._ -import org.specs2.control.NoLanguageFeatures import org.specs2.specification.Scope import play.api.libs.json.Json import play.api.mvc._ @@ -43,7 +42,7 @@ import scala.language.postfixOps /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.authenticators.SessionAuthenticator]]. */ -class SessionAuthenticatorSpec extends PlaySpecification with NoLanguageFeatures { +class SessionAuthenticatorSpec extends PlaySpecification { "The `isValid` method of the authenticator" should { "return false if the authenticator is expired" in new Context { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala index 56c4e9b0..636fac73 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala @@ -28,6 +28,7 @@ import play.api.mvc.AnyContentAsEmpty import play.api.test.{ FakeRequest, WithApplication } import test.Helper import org.mockito.Mockito.* +import org.mockito.ArgumentMatchers.any import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.{ ExecutionContext, Future } @@ -65,17 +66,17 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { } } - "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { + "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication { val wsRequest = mock(classOf[MockWSRequest]) val wsResponse = mock(classOf[MockWSRequest#Response]) implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") when(wsResponse.status).thenReturn(200) when(wsResponse.json).thenReturn(Json.obj()) when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any))).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL))).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]))).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext]))).thenReturn(Future.successful(state)) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) failed[UnexpectedResponseException](provider.authenticate()) { case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) @@ -86,13 +87,13 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock(classOf[MockWSRequest]) val wsResponse = mock(classOf[MockWSRequest#Response]) implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status)).thenReturn(200) - when(wsResponse.json)).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any))).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any))).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL))).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext]))).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext]))).thenReturn(Future.successful(state)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) } @@ -105,7 +106,7 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { when(wsResponse.status).thenReturn(400) when(wsResponse.json).thenReturn(Helper.loadJson("providers/custom/facebook.error.json")) when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token")))).thenReturn(wsRequest) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { case e => e.getMessage must equalTo(SpecifiedProfileError.format( @@ -119,9 +120,9 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { val wsRequest = mock(classOf[MockWSRequest]) val wsResponse = mock(classOf[MockWSRequest#Response]) - when(wsResponse.status)).thenReturn(500) + when(wsResponse.status).thenReturn(500) wsResponse.json throws new RuntimeException("") - when(wsRequest.get())).thenReturn(Future.successful(wsResponse)) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { @@ -132,8 +133,8 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { "return the social profile" in new WithApplication with Context { val wsRequest = mock(classOf[MockWSRequest]) val wsResponse = mock(classOf[MockWSRequest#Response]) - when(wsResponse.status)).thenReturn(200) - when(wsResponse.json)).thenReturn(Helper.loadJson("providers/custom/facebook.success.json")) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/custom/facebook.success.json")) when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) @@ -157,34 +158,7 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { */ override protected def context: OAuth2ProviderSpecContext = new Context {} - /** - * The context. - */ - trait Context extends OAuth2ProviderSpecContext { - - /** - * The OAuth2 settings. - */ - override lazy val oAuthSettings = org.mockito.Mockito.spy(OAuth2Settings( - authorizationURL = Some("https://graph.facebook.com/oauth/authorize"), - accessTokenURL = "https://graph.facebook.com/oauth/access_token", - redirectURL = Some("https://www.mohiva.com"), - clientID = "my.client.id", - clientSecret = "my.client.secret", - scope = Some("email"))) - - /** - * The OAuth2 info returned by Facebook. - * - * @see https://developers.facebook.com/docs/facebook-login/access-tokens - */ - override lazy val oAuthInfo = Helper.loadJson("providers/oauth2/facebook.access.token.json") - /** - * The provider to test. - */ - lazy val provider = new CustomFacebookProvider(httpLayer, stateProvider, oAuthSettings) - } /** * A custom social profile for testing purpose. diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala index 515c31ea..979819a1 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala @@ -23,7 +23,6 @@ import io.github.honeycombcheesecake.play.silhouette.impl.exceptions.OAuth1Token import io.github.honeycombcheesecake.play.silhouette.impl.providers.OAuth1Info import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.secrets.CookieSecret._ import io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.secrets.CookieSecretProvider._ -import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.JsonMatchers import org.specs2.specification.Scope import play.api.mvc.{ AnyContentAsEmpty, Cookie, Results } @@ -42,7 +41,7 @@ import scala.util.{ Failure, Success } /** * Test case for the [[io.github.honeycombcheesecake.play.silhouette.impl.providers.oauth1.secrets.CookieSecret]] class. */ -class CookieSecretSpec extends PlaySpecification with JsonMatchers with NoLanguageFeatures { +class CookieSecretSpec extends PlaySpecification with JsonMatchers { "The `isExpired` method of the secret" should { "return true if the secret is expired" in new Context { From 46243f1e622eace7f420cba6f073bfbf6b293c9f Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Mon, 27 Nov 2023 11:27:28 +0100 Subject: [PATCH 12/30] Fixes --- .../play/silhouette/api/ErrorHandlerSpec.scala | 2 +- .../impl/providers/OAuth2ProviderSpec.scala | 4 ++-- .../impl/providers/OpenIDProviderSpec.scala | 2 +- .../impl/util/DefaultFingerprintGeneratorSpec.scala | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala index c773ec22..d72eb0c6 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala @@ -193,7 +193,7 @@ class ErrorHandlerSpec extends PlaySpecification { expectedMessage: String, f: RequestHeader => Future[Result]) = { implicit val request = acceptedMediaType match { - case Some(mediaType) => FakeRequest().withHeaders(ACCEPT -> mediaType) + case Some(mediaType) => FakeRequest().withHeaders((ACCEPT, mediaType)) case None => FakeRequest() } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala index 6ce7fb20..8f978d88 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala @@ -154,7 +154,7 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So implicit val req = FakeRequest[AnyContent]( method = GET, uri = "/request-path/something", - headers = FakeHeaders(Seq(HeaderNames.HOST -> "www.example.com")), + headers = FakeHeaders(Seq((HeaderNames.HOST, "www.example.com"))), body = AnyContentAsEmpty, secure = secure) @@ -187,7 +187,7 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So implicit val req = FakeRequest[AnyContent]( method = GET, uri = "/request-path/something", - headers = FakeHeaders(Seq(HeaderNames.HOST -> "www.example.com")), + headers = FakeHeaders(Seq((HeaderNames.HOST, "www.example.com"))), body = AnyContentAsEmpty, secure = secure) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala index d454c205..6f66d4da 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala @@ -88,7 +88,7 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] { implicit val req = FakeRequest[AnyContent]( method = GET, uri = "/request-path/something", - headers = FakeHeaders(Seq(HeaderNames.HOST -> "www.example.com")), + headers = FakeHeaders(Seq((HeaderNames.HOST, "www.example.com"))), body = AnyContentAsEmpty, secure = secure) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/DefaultFingerprintGeneratorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/DefaultFingerprintGeneratorSpec.scala index 1a463dd8..3ef16ea7 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/DefaultFingerprintGeneratorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/util/DefaultFingerprintGeneratorSpec.scala @@ -27,7 +27,7 @@ class DefaultFingerprintGeneratorSpec extends PlaySpecification { "return fingerprint including the `User-Agent` header" in { val userAgent = "test-user-agent" val generator = new DefaultFingerprintGenerator() - implicit val request = FakeRequest().withHeaders(USER_AGENT -> userAgent) + implicit val request = FakeRequest().withHeaders((USER_AGENT, userAgent)) generator.generate must be equalTo Hash.sha1(userAgent + ":::") } @@ -35,7 +35,7 @@ class DefaultFingerprintGeneratorSpec extends PlaySpecification { "return fingerprint including the `Accept-Language` header" in { val acceptLanguage = "test-accept-language" val generator = new DefaultFingerprintGenerator() - implicit val request = FakeRequest().withHeaders(ACCEPT_LANGUAGE -> acceptLanguage) + implicit val request = FakeRequest().withHeaders((ACCEPT_LANGUAGE, acceptLanguage)) generator.generate must be equalTo Hash.sha1(":" + acceptLanguage + "::") } @@ -43,7 +43,7 @@ class DefaultFingerprintGeneratorSpec extends PlaySpecification { "return fingerprint including the `Accept-Charset` header" in { val acceptCharset = "test-accept-charset" val generator = new DefaultFingerprintGenerator() - implicit val request = FakeRequest().withHeaders(ACCEPT_CHARSET -> acceptCharset) + implicit val request = FakeRequest().withHeaders((ACCEPT_CHARSET, acceptCharset)) generator.generate must be equalTo Hash.sha1("::" + acceptCharset + ":") } @@ -61,9 +61,9 @@ class DefaultFingerprintGeneratorSpec extends PlaySpecification { val acceptCharset = "test-accept-charset" val generator = new DefaultFingerprintGenerator(true) implicit val request = FakeRequest().withHeaders( - USER_AGENT -> userAgent, - ACCEPT_LANGUAGE -> acceptLanguage, - ACCEPT_CHARSET -> acceptCharset) + (USER_AGENT, userAgent), + (ACCEPT_LANGUAGE, acceptLanguage), + (ACCEPT_CHARSET, acceptCharset)) generator.generate must be equalTo Hash.sha1( userAgent + ":" + acceptLanguage + ":" + acceptCharset + ":127.0.0.1") From 826ff5b3b9caab1ee10f4bb791dbb1bf94308730 Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Mon, 27 Nov 2023 11:58:36 +0100 Subject: [PATCH 13/30] Rollback removal of context --- .../custom/FacebookProviderSpec.scala | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala index 636fac73..e991be80 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala @@ -158,7 +158,34 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { */ override protected def context: OAuth2ProviderSpecContext = new Context {} + /** + * The context. + */ + trait Context extends OAuth2ProviderSpecContext { + + /** + * The OAuth2 settings. + */ + override lazy val oAuthSettings = spy(OAuth2Settings( + authorizationURL = Some("https://graph.facebook.com/oauth/authorize"), + accessTokenURL = "https://graph.facebook.com/oauth/access_token", + redirectURL = Some("https://www.mohiva.com"), + clientID = "my.client.id", + clientSecret = "my.client.secret", + scope = Some("email"))) + + /** + * The OAuth2 info returned by Facebook. + * + * @see https://developers.facebook.com/docs/facebook-login/access-tokens + */ + override lazy val oAuthInfo = Helper.loadJson("providers/oauth2/facebook.access.token.json") + /** + * The provider to test. + */ + lazy val provider = new CustomFacebookProvider(httpLayer, stateProvider, oAuthSettings) + } /** * A custom social profile for testing purpose. From 16e9d27ee5395746fd92d2f809a04584b9a0ba60 Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Mon, 27 Nov 2023 15:37:33 +0100 Subject: [PATCH 14/30] Fix some compilation errors --- .../play/silhouette/impl/providers/OAuth1ProviderSpec.scala | 2 +- .../impl/providers/custom/FacebookProviderSpec.scala | 5 +++-- .../impl/providers/oauth1/LinkedInProviderSpec.scala | 4 ++-- .../impl/providers/oauth1/TwitterProviderSpec.scala | 4 ++-- .../silhouette/impl/providers/oauth1/XingProviderSpec.scala | 4 ++-- .../silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala | 4 ++-- .../impl/providers/oauth2/DropboxProviderSpec.scala | 4 ++-- .../impl/providers/oauth2/FacebookProviderSpec.scala | 4 ++-- .../impl/providers/oauth2/FoursquareProviderSpec.scala | 4 ++-- .../impl/providers/oauth2/GitHubProviderSpec.scala | 4 ++-- .../impl/providers/oauth2/GitLabProviderSpec.scala | 4 ++-- .../impl/providers/oauth2/GoogleProviderSpec.scala | 4 ++-- .../impl/providers/oauth2/InstagramProviderSpec.scala | 4 ++-- .../impl/providers/oauth2/LinkedInProviderSpec.scala | 4 ++-- .../silhouette/impl/providers/oauth2/VKProviderSpec.scala | 4 ++-- .../silhouette/impl/providers/openid/SteamProviderSpec.scala | 4 ++-- .../silhouette/impl/providers/openid/YahooProviderSpec.scala | 4 ++-- 17 files changed, 34 insertions(+), 33 deletions(-) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala index 54ebf2e7..eaf91b99 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala @@ -97,7 +97,7 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] { implicit val req = FakeRequest[AnyContent]( method = GET, uri = "/request-path/something", - headers = FakeHeaders(Seq(HeaderNames.HOST -> "www.example.com")), + headers = FakeHeaders(Seq((HeaderNames.HOST, "www.example.com"))), body = AnyContentAsEmpty, secure = secure) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala index e991be80..b4215535 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala @@ -29,6 +29,7 @@ import play.api.test.{ FakeRequest, WithApplication } import test.Helper import org.mockito.Mockito.* import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyString import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.{ ExecutionContext, Future } @@ -40,7 +41,7 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = provider.withSettings { s => + val s: CustomFacebookProvider = provider.withSettings { s => s.copy(accessTokenURL = "new-access-token-url") } @@ -184,7 +185,7 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { /** * The provider to test. */ - lazy val provider = new CustomFacebookProvider(httpLayer, stateProvider, oAuthSettings) + lazy val provider: CustomFacebookProvider = new CustomFacebookProvider(httpLayer, stateProvider, oAuthSettings) } /** diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala index 0dc7e66f..3d6bb230 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala @@ -39,7 +39,7 @@ class LinkedInProviderSpec extends OAuth1ProviderSpec { val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s => s.copy("new-request-token-url") } - val s = provider.withSettings(overrideSettingsFunction) + val s: LinkedInProvider = provider.withSettings(overrideSettingsFunction) s.settings.requestTokenURL must be equalTo "new-request-token-url" verify(oAuthService).withSettings(overrideSettingsFunction) @@ -140,6 +140,6 @@ class LinkedInProviderSpec extends OAuth1ProviderSpec { /** * The provider to test. */ - lazy val provider = new LinkedInProvider(httpLayer, oAuthService, oAuthTokenSecretProvider, oAuthSettings) + lazy val provider: LinkedInProvider = new LinkedInProvider(httpLayer, oAuthService, oAuthTokenSecretProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala index 5b943df6..76c1811c 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala @@ -39,7 +39,7 @@ class TwitterProviderSpec extends OAuth1ProviderSpec { val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s => s.copy("new-request-token-url") } - val s = provider.withSettings(overrideSettingsFunction) + val s: TwitterProvider = provider.withSettings(overrideSettingsFunction) s.settings.requestTokenURL must be equalTo "new-request-token-url" verify(oAuthService).withSettings(overrideSettingsFunction) @@ -151,6 +151,6 @@ class TwitterProviderSpec extends OAuth1ProviderSpec { /** * The provider to test. */ - lazy val provider = new TwitterProvider(httpLayer, oAuthService, oAuthTokenSecretProvider, oAuthSettings) + lazy val provider: TwitterProvider = new TwitterProvider(httpLayer, oAuthService, oAuthTokenSecretProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala index 18c0b995..7e3dcad7 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala @@ -39,7 +39,7 @@ class XingProviderSpec extends OAuth1ProviderSpec { val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s => s.copy("new-request-token-url") } - val s = provider.withSettings(overrideSettingsFunction) + val s: XingProvider = provider.withSettings(overrideSettingsFunction) s.settings.requestTokenURL must be equalTo "new-request-token-url" verify(oAuthService).withSettings(overrideSettingsFunction) @@ -137,6 +137,6 @@ class XingProviderSpec extends OAuth1ProviderSpec { /** * The provider to test. */ - lazy val provider = new XingProvider(httpLayer, oAuthService, oAuthTokenSecretProvider, oAuthSettings) + lazy val provider: XingProvider = new XingProvider(httpLayer, oAuthService, oAuthTokenSecretProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala index 70954396..0a15fbb5 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala @@ -38,7 +38,7 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = provider.withSettings { s => + val s: Auth0Provider = provider.withSettings { s => s.copy(accessTokenURL = "new-access-token-url") } @@ -218,6 +218,6 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { /** * The provider to test. */ - lazy val provider = new Auth0Provider(httpLayer, stateProvider, oAuthSettings) + lazy val provider: Auth0Provider = new Auth0Provider(httpLayer, stateProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala index 5df9a180..fbd79daa 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala @@ -39,7 +39,7 @@ class DropboxProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = provider.withSettings { s => + val s: DropboxProvider = provider.withSettings { s => s.copy(accessTokenURL = "new-access-token-url") } @@ -226,6 +226,6 @@ class DropboxProviderSpec extends OAuth2ProviderSpec { /** * The provider to test. */ - lazy val provider = new DropboxProvider(httpLayer, stateProvider, oAuthSettings) + lazy val provider: DropboxProvider = new DropboxProvider(httpLayer, stateProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala index d0950016..bd8eff30 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala @@ -39,7 +39,7 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = provider.withSettings { s => + val s: FacebookProvider = provider.withSettings { s => s.copy(accessTokenURL = "new-access-token-url") } @@ -221,6 +221,6 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { /** * The provider to test. */ - lazy val provider = new FacebookProvider(httpLayer, stateProvider, oAuthSettings) + lazy val provider: FacebookProvider = new FacebookProvider(httpLayer, stateProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala index ea9ca526..e2df277f 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala @@ -39,7 +39,7 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = provider.withSettings { s => + val s: FoursquareProvider = provider.withSettings { s => s.copy(accessTokenURL = "new-access-token-url") } @@ -279,6 +279,6 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { /** * The provider to test. */ - lazy val provider = new FoursquareProvider(httpLayer, stateProvider, oAuthSettings) + lazy val provider: FoursquareProvider = new FoursquareProvider(httpLayer, stateProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala index e1ec3ce7..e705ab9e 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala @@ -40,7 +40,7 @@ class GitHubProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = provider.withSettings { s => + val s: GitHubProvider = provider.withSettings { s => s.copy(accessTokenURL = "new-access-token-url") } @@ -227,6 +227,6 @@ class GitHubProviderSpec extends OAuth2ProviderSpec { /** * The provider to test. */ - lazy val provider = new GitHubProvider(httpLayer, stateProvider, oAuthSettings) + lazy val provider: GitHubProvider = new GitHubProvider(httpLayer, stateProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala index 92a90687..01ba36e6 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala @@ -39,7 +39,7 @@ class GitLabProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = provider.withSettings { s => + val s: GitLabProvider = provider.withSettings { s => s.copy(accessTokenURL = "new-access-token-url") } @@ -217,7 +217,7 @@ class GitLabProviderSpec extends OAuth2ProviderSpec { /** * The provider to test. */ - lazy val provider = new GitLabProvider(httpLayer, stateProvider, oAuthSettings) + lazy val provider: GitLabProvider = new GitLabProvider(httpLayer, stateProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala index 26365c27..6bd827d3 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala @@ -39,7 +39,7 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = provider.withSettings { s => + val s: GoogleProvider = provider.withSettings { s => s.copy(accessTokenURL = "new-access-token-url") } @@ -293,6 +293,6 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { /** * The provider to test. */ - lazy val provider = new GoogleProvider(httpLayer, stateProvider, oAuthSettings) + lazy val provider: GoogleProvider = new GoogleProvider(httpLayer, stateProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala index a6289fd2..6e363ab6 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala @@ -39,7 +39,7 @@ class InstagramProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = provider.withSettings { s => + val s: InstagramProvider = provider.withSettings { s => s.copy(accessTokenURL = "new-access-token-url") } @@ -218,6 +218,6 @@ class InstagramProviderSpec extends OAuth2ProviderSpec { /** * The provider to test. */ - lazy val provider = new InstagramProvider(httpLayer, stateProvider, oAuthSettings) + lazy val provider: InstagramProvider = new InstagramProvider(httpLayer, stateProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala index ec02ea7d..b5919cc8 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala @@ -39,7 +39,7 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = provider.withSettings { s => + val s: LinkedInProvider = provider.withSettings { s => s.copy(accessTokenURL = "new-access-token-url") } @@ -244,6 +244,6 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec { /** * The provider to test. */ - lazy val provider = new LinkedInProvider(httpLayer, stateProvider, oAuthSettings) + lazy val provider: LinkedInProvider = new LinkedInProvider(httpLayer, stateProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala index 56cddf8d..b2bbfe96 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala @@ -39,7 +39,7 @@ class VKProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = provider.withSettings { s => + val s: VKProvider = provider.withSettings { s => s.copy(accessTokenURL = "new-access-token-url") } @@ -273,6 +273,6 @@ class VKProviderSpec extends OAuth2ProviderSpec { /** * The provider to test. */ - lazy val provider = new VKProvider(httpLayer, stateProvider, oAuthSettings) + lazy val provider: VKProvider = new VKProvider(httpLayer, stateProvider, oAuthSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala index 6127b19e..7a327f44 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala @@ -30,7 +30,7 @@ class SteamProviderSpec extends OpenIDProviderSpec { val overrideSettingsFunction: OpenIDSettings => OpenIDSettings = { s => s.copy("new-provider-url") } - val s = provider.withSettings(overrideSettingsFunction) + val s: SteamProvider = provider.withSettings(overrideSettingsFunction) s.settings.providerURL must be equalTo "new-provider-url" verify(openIDService).withSettings(overrideSettingsFunction) @@ -74,6 +74,6 @@ class SteamProviderSpec extends OpenIDProviderSpec { /** * The provider to test. */ - lazy val provider = new SteamProvider(httpLayer, openIDService, openIDSettings) + lazy val provider: SteamProvider = new SteamProvider(httpLayer, openIDService, openIDSettings) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala index fb654ebb..32995b78 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala @@ -30,7 +30,7 @@ class YahooProviderSpec extends OpenIDProviderSpec { val overrideSettingsFunction: OpenIDSettings => OpenIDSettings = { s => s.copy("new-provider-url") } - val s = provider.withSettings(overrideSettingsFunction) + val s: YahooProvider = provider.withSettings(overrideSettingsFunction) s.settings.providerURL must be equalTo "new-provider-url" verify(openIDService).withSettings(overrideSettingsFunction) @@ -85,6 +85,6 @@ class YahooProviderSpec extends OpenIDProviderSpec { /** * The provider to test. */ - lazy val provider = new YahooProvider(httpLayer, openIDService, openIDSettings) + lazy val provider: YahooProvider = new YahooProvider(httpLayer, openIDService, openIDSettings) } } From 85f635dd469230ee104ba940fe17b98e36ed9acf Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Mon, 27 Nov 2023 15:53:33 +0100 Subject: [PATCH 15/30] Fixes --- .../play/silhouette/test/FakesSpec.scala | 12 ++++++------ .../impl/providers/OAuth2ProviderSpec.scala | 6 +++--- .../impl/providers/custom/FacebookProviderSpec.scala | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala b/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala index 00461e08..484770cb 100644 --- a/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala +++ b/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala @@ -16,17 +16,17 @@ package io.github.honeycombcheesecake.play.silhouette.test import javax.inject.Inject - -import io.github.honeycombcheesecake.play.silhouette.api._ -import io.github.honeycombcheesecake.play.silhouette.impl.authenticators._ -import io.github.honeycombcheesecake.play.silhouette.test.FakesSpec._ +import io.github.honeycombcheesecake.play.silhouette.api.* +import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.* +import io.github.honeycombcheesecake.play.silhouette.test.FakesSpec.* import net.codingwell.scalaguice.ScalaModule import org.specs2.matcher.JsonMatchers import org.specs2.specification.Scope +import play.api.Application import play.api.inject.guice.GuiceApplicationBuilder import play.api.libs.json.Json -import play.api.mvc.{ AbstractController, AnyContentAsEmpty, ControllerComponents } -import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } +import play.api.mvc.{AbstractController, AnyContentAsEmpty, ControllerComponents} +import play.api.test.{FakeRequest, PlaySpecification, WithApplication} import scala.concurrent.ExecutionContext.Implicits.global diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala index 8f978d88..a70821e5 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala @@ -173,7 +173,7 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So } result(c.provider.authenticate()) { result => - redirectLocation(result) must beSome.which { url => + redirectLocation(result) must beSome.which { url: String => url must contain(s"$RedirectURI=${encode(resolvedRedirectURL, "UTF-8")}") } } @@ -208,13 +208,13 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So redirectURL match { case Some(_) => result(c.provider.authenticate()) { result => - redirectLocation(result) must beSome.which { url => + redirectLocation(result) must beSome.which { url: String => url must contain(s"$RedirectURI=${encode(resolvedRedirectURL, "UTF-8")}") } } case None => result(c.provider.authenticate()) { result => - redirectLocation(result) must beSome.which { url => + redirectLocation(result) must beSome.which { url: String => url must not contain s"$RedirectURI=" } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala index b4215535..fad9a93d 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala @@ -67,7 +67,7 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { } } - "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication { + "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { val wsRequest = mock(classOf[MockWSRequest]) val wsResponse = mock(classOf[MockWSRequest#Response]) implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") @@ -122,7 +122,7 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { val wsRequest = mock(classOf[MockWSRequest]) val wsResponse = mock(classOf[MockWSRequest#Response]) when(wsResponse.status).thenReturn(500) - wsResponse.json throws new RuntimeException("") + when(wsResponse.json).thenThrow(new RuntimeException("")) when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) From 02ca153f25b11fa42f74bd082433df62661a26b4 Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Mon, 27 Nov 2023 17:21:50 +0100 Subject: [PATCH 16/30] Fix other specs2 errors --- .../silhouette/impl/providers/OAuth1ProviderSpec.scala | 2 +- .../silhouette/impl/providers/OAuth2ProviderSpec.scala | 10 +++++----- .../silhouette/impl/providers/OpenIDProviderSpec.scala | 4 ++-- .../impl/providers/SocialProviderRegistrySpec.scala | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala index eaf91b99..6896921a 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala @@ -81,7 +81,7 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] { result(c.provider.authenticate()) { result => status(result) must equalTo(SEE_OTHER) - redirectLocation(result) must beSome.which(_ == c.oAuthSettings.authorizationURL) + redirectLocation(result) must beSome[String].which(_ == c.oAuthSettings.authorizationURL) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala index a70821e5..861d521b 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala @@ -99,7 +99,7 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So result(c.provider.authenticate()) { result => status(result) must equalTo(SEE_OTHER) session(result).get(sessionKey) must beSome(c.stateProvider.serialize(c.state)) - redirectLocation(result) must beSome.which { url => + redirectLocation(result) must beSome[String].which { url => val urlParams = c.urlParams(url) val redirectParam = c.oAuthSettings.redirectURL match { case Some(rUri) => List((RedirectURI, rUri)) @@ -173,7 +173,7 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So } result(c.provider.authenticate()) { result => - redirectLocation(result) must beSome.which { url: String => + redirectLocation(result) must beSome[String].which { url => url must contain(s"$RedirectURI=${encode(resolvedRedirectURL, "UTF-8")}") } } @@ -208,13 +208,13 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So redirectURL match { case Some(_) => result(c.provider.authenticate()) { result => - redirectLocation(result) must beSome.which { url: String => + redirectLocation(result) must beSome[String].which { url => url must contain(s"$RedirectURI=${encode(resolvedRedirectURL, "UTF-8")}") } } case None => result(c.provider.authenticate()) { result => - redirectLocation(result) must beSome.which { url: String => + redirectLocation(result) must beSome[String].which { url => url must not contain s"$RedirectURI=" } } @@ -236,7 +236,7 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So } result(c.provider.authenticate())(result => - redirectLocation(result) must beSome.which(_ must not contain State)) + redirectLocation(result) must beSome[String].which(_ must not contain State)) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala index 6f66d4da..f27ed5fd 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala @@ -58,7 +58,7 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] { result(c.provider.authenticate()) { result => status(result) must equalTo(SEE_OTHER) - redirectLocation(result) must beSome.which(_ == c.openIDSettings.providerURL) + redirectLocation(result) must beSome[String].which(_ == c.openIDSettings.providerURL) } } @@ -68,7 +68,7 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] { result(c.provider.authenticate()) { result => status(result) must equalTo(SEE_OTHER) - redirectLocation(result) must beSome.which(_ == c.openIDSettings.providerURL) + redirectLocation(result) must beSome[String].which(_ == c.openIDSettings.providerURL) } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala index 7b10feaf..7cebdb84 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala @@ -40,7 +40,7 @@ class SocialProviderRegistrySpec extends PlaySpecification { "return a provider by its ID as SocialProvider" in new Context { val provider = registry.get[SocialProvider](GoogleProvider.ID) - provider must beSome.like { + provider must beSome[SocialProvider].like { case value => value.id must be equalTo providers(1).id value must beAnInstanceOf[SocialProvider] @@ -50,7 +50,7 @@ class SocialProviderRegistrySpec extends PlaySpecification { "return a provider by its ID as OAuth2Provider" in new Context { val provider = registry.get[OAuth2Provider](GoogleProvider.ID) - provider must beSome.like { + provider must beSome[OAuth2Provider].like { case value => value.id must be equalTo providers(1).id value must beAnInstanceOf[OAuth2Provider] From 4a9c6edf0be8b3faa4bd40f8bb92042af9f13b6b Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Tue, 28 Nov 2023 09:31:48 +0100 Subject: [PATCH 17/30] Fix overloading errors --- .../play/silhouette/test/FakesSpec.scala | 7 ++++--- .../play/silhouette/api/actions/SecuredActionSpec.scala | 2 +- .../play/silhouette/api/actions/UnsecuredActionSpec.scala | 2 +- .../play/silhouette/api/actions/UserAwareActionSpec.scala | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala b/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala index 484770cb..b0582d04 100644 --- a/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala +++ b/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala @@ -17,6 +17,7 @@ package io.github.honeycombcheesecake.play.silhouette.test import javax.inject.Inject import io.github.honeycombcheesecake.play.silhouette.api.* +import io.github.honeycombcheesecake.play.silhouette.api.actions.{SecuredRequest, UserAwareRequest} import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.* import io.github.honeycombcheesecake.play.silhouette.test.FakesSpec.* import net.codingwell.scalaguice.ScalaModule @@ -25,7 +26,7 @@ import org.specs2.specification.Scope import play.api.Application import play.api.inject.guice.GuiceApplicationBuilder import play.api.libs.json.Json -import play.api.mvc.{AbstractController, AnyContentAsEmpty, ControllerComponents} +import play.api.mvc.{AbstractController, AnyContent, AnyContentAsEmpty, ControllerComponents} import play.api.test.{FakeRequest, PlaySpecification, WithApplication} import scala.concurrent.ExecutionContext.Implicits.global @@ -363,7 +364,7 @@ object FakesSpec { * * @return The result to send to the client. */ - def defaultSecuredAction = silhouette.SecuredAction { implicit request => + def defaultSecuredAction = silhouette.SecuredAction { implicit request: SecuredRequest[CookieEnv, AnyContent] => Ok(Json.toJson(request.identity.loginInfo)) } @@ -372,7 +373,7 @@ object FakesSpec { * * @return The result to send to the client. */ - def defaultUserAwareAction = silhouette.UserAwareAction { implicit request => + def defaultUserAwareAction = silhouette.UserAwareAction { implicit request: UserAwareRequest[CookieEnv, AnyContent] => request.identity match { case Some(identity) => Ok(Json.toJson(identity.loginInfo)) case None => Unauthorized diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala index 7ab2a36c..d1887517 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala @@ -756,7 +756,7 @@ object SecuredActionSpec { * * @return The result to send to the client. */ - def defaultAction = silhouette.SecuredAction { implicit request => + def defaultAction = silhouette.SecuredAction { implicit request: SecuredRequest[SecuredEnv, AnyContent] => render { case Accepts.Json() => Ok(Json.obj("result" -> "full.access")) case Accepts.Html() => Ok("full.access") diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala index ec872c96..f8227ab7 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala @@ -379,7 +379,7 @@ object UnsecuredActionSpec { * An unsecured request handler. */ def defaultHandler = Action.async { implicit request => - silhouette.UnsecuredRequestHandler { _ => + silhouette.UnsecuredRequestHandler { (_: Request[AnyContent]) => Future.successful(HandlerResult(Ok, Some("data"))) }.map { case HandlerResult(r, Some(data)) => Ok(data) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala index e5ebbe21..b0b8ed65 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala @@ -434,7 +434,7 @@ object UserAwareActionSpec { * * @return The result to send to the client. */ - def defaultAction = silhouette.UserAwareAction { implicit request => + def defaultAction = silhouette.UserAwareAction { implicit request: UserAwareRequest[UserAwareEnv, AnyContent] => if (request.identity.isDefined && request.authenticator.isDefined) { Ok("with.identity.and.authenticator") } else if (request.authenticator.isDefined) { From 950dc397ffc49cbe5b2569c50611f0a9e4cff076 Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Tue, 28 Nov 2023 11:17:32 +0100 Subject: [PATCH 18/30] Fix imports --- .../play/silhouette/test/FakesSpec.scala | 14 +++++++------- .../silhouette/api/actions/SecuredAction.scala | 5 +++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala b/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala index b0582d04..731f2ae1 100644 --- a/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala +++ b/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala @@ -16,18 +16,18 @@ package io.github.honeycombcheesecake.play.silhouette.test import javax.inject.Inject -import io.github.honeycombcheesecake.play.silhouette.api.* -import io.github.honeycombcheesecake.play.silhouette.api.actions.{SecuredRequest, UserAwareRequest} -import io.github.honeycombcheesecake.play.silhouette.impl.authenticators.* -import io.github.honeycombcheesecake.play.silhouette.test.FakesSpec.* + +import io.github.honeycombcheesecake.play.silhouette.api._ +import io.github.honeycombcheesecake.play.silhouette.api.actions.{ SecuredRequest, UserAwareRequest } +import io.github.honeycombcheesecake.play.silhouette.impl.authenticators._ +import io.github.honeycombcheesecake.play.silhouette.test.FakesSpec._ import net.codingwell.scalaguice.ScalaModule import org.specs2.matcher.JsonMatchers import org.specs2.specification.Scope -import play.api.Application import play.api.inject.guice.GuiceApplicationBuilder import play.api.libs.json.Json -import play.api.mvc.{AbstractController, AnyContent, AnyContentAsEmpty, ControllerComponents} -import play.api.test.{FakeRequest, PlaySpecification, WithApplication} +import play.api.mvc.{ AbstractController, AnyContent, AnyContentAsEmpty, ControllerComponents } +import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } import scala.concurrent.ExecutionContext.Implicits.global diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala index 4c264759..a8e3bbc7 100644 --- a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala +++ b/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala @@ -20,14 +20,15 @@ package io.github.honeycombcheesecake.play.silhouette.api.actions import javax.inject.Inject + import io.github.honeycombcheesecake.play.silhouette.api._ import play.api.i18n.MessagesApi import play.api.inject.Module import play.api.mvc._ -import play.api.{Configuration, Environment => PlayEnv} +import play.api.{ Configuration, Environment => PlayEnv } import scala.annotation.nowarn -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.{ ExecutionContext, Future } import scala.reflect.ClassTag /** From 88780fdf03e7f3a73d42b1d0f120a11bc02249f9 Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Tue, 28 Nov 2023 11:53:53 +0100 Subject: [PATCH 19/30] Fix guice implicit conversions --- .../play/silhouette/test/FakesSpec.scala | 7 +++++-- .../play/silhouette/api/actions/SecuredActionSpec.scala | 7 +++++-- .../play/silhouette/api/actions/UnsecuredActionSpec.scala | 7 +++++-- .../play/silhouette/api/actions/UserAwareActionSpec.scala | 7 +++++-- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala b/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala index 731f2ae1..f95891b7 100644 --- a/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala +++ b/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala @@ -15,6 +15,8 @@ */ package io.github.honeycombcheesecake.play.silhouette.test +import com.google.inject.AbstractModule + import javax.inject.Inject import io.github.honeycombcheesecake.play.silhouette.api._ @@ -25,6 +27,7 @@ import net.codingwell.scalaguice.ScalaModule import org.specs2.matcher.JsonMatchers import org.specs2.specification.Scope import play.api.inject.guice.GuiceApplicationBuilder +import play.api.inject.guice.GuiceableModule import play.api.libs.json.Json import play.api.mvc.{ AbstractController, AnyContent, AnyContentAsEmpty, ControllerComponents } import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } @@ -288,13 +291,13 @@ class FakesSpec extends PlaySpecification with JsonMatchers { * The guice application builder. */ lazy val app = new GuiceApplicationBuilder() - .bindings(new GuiceModule) + .bindings(GuiceableModule.guiceable(new GuiceModule)) .build() /** * The guice module. */ - class GuiceModule extends ScalaModule { + class GuiceModule extends AbstractModule with ScalaModule { override def configure(): Unit = { bind[Silhouette[CookieEnv]].to[SilhouetteProvider[CookieEnv]] bind[Environment[CookieEnv]].toInstance(env) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala index d1887517..87d944ce 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala @@ -15,6 +15,8 @@ */ package io.github.honeycombcheesecake.play.silhouette.api.actions +import com.google.inject.AbstractModule + import javax.inject.Inject import akka.actor.{ Actor, ActorSystem, Props } @@ -28,6 +30,7 @@ import org.specs2.matcher.JsonMatchers import org.specs2.specification.Scope import play.api.inject.bind import play.api.inject.guice.GuiceApplicationBuilder +import play.api.inject.guice.GuiceableModule import play.api.libs.json.Json import play.api.mvc.Results._ import play.api.mvc._ @@ -536,14 +539,14 @@ class SecuredActionSpec extends PlaySpecification with JsonMatchers { * The guice application builder. */ lazy val app = new GuiceApplicationBuilder() - .bindings(new GuiceModule) + .bindings(GuiceableModule.guiceable(new GuiceModule)) .overrides(bind[SecuredErrorHandler].to[GlobalSecuredErrorHandler]) .build() /** * The guice module. */ - class GuiceModule extends ScalaModule { + class GuiceModule extends AbstractModule with ScalaModule { override def configure(): Unit = { bind[Environment[SecuredEnv]].toInstance(env) bind[Authorization[SecuredEnv#I, SecuredEnv#A]].toInstance(authorization) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala index f8227ab7..8ee05b63 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala @@ -15,6 +15,8 @@ */ package io.github.honeycombcheesecake.play.silhouette.api.actions +import com.google.inject.AbstractModule + import javax.inject.Inject import akka.actor.{ Actor, ActorSystem, Props } @@ -28,6 +30,7 @@ import org.specs2.matcher.JsonMatchers import org.specs2.specification.Scope import play.api.inject.bind import play.api.inject.guice.GuiceApplicationBuilder +import play.api.inject.guice.GuiceableModule import play.api.mvc.Results._ import play.api.mvc._ import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } @@ -186,14 +189,14 @@ class UnsecuredActionSpec extends PlaySpecification with JsonMatchers { * The guice application builder. */ lazy val app = new GuiceApplicationBuilder() - .bindings(new GuiceModule) + .bindings(GuiceableModule.guiceable(new GuiceModule)) .overrides(bind[UnsecuredErrorHandler].to[GlobalUnsecuredErrorHandler]) .build() /** * The guice module. */ - class GuiceModule extends ScalaModule { + class GuiceModule extends AbstractModule with ScalaModule { override def configure(): Unit = { bind[Environment[UnsecuredEnv]].toInstance(env) bind[Silhouette[UnsecuredEnv]].to[SilhouetteProvider[UnsecuredEnv]] diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala index b0b8ed65..8566a524 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala @@ -15,6 +15,8 @@ */ package io.github.honeycombcheesecake.play.silhouette.api.actions +import com.google.inject.AbstractModule + import javax.inject.Inject import io.github.honeycombcheesecake.play.silhouette.api._ @@ -25,6 +27,7 @@ import org.specs2.matcher.JsonMatchers import org.specs2.specification.Scope import play.api.i18n.{ Lang, Langs, MessagesApi } import play.api.inject.guice.GuiceApplicationBuilder +import play.api.inject.guice.GuiceableModule import play.api.libs.json.Json import play.api.mvc.{ ControllerComponents, _ } import play.api.test.{ FakeRequest, PlaySpecification, WithApplication } @@ -303,13 +306,13 @@ class UserAwareActionSpec extends PlaySpecification with JsonMatchers { * The guice application builder. */ lazy val app = new GuiceApplicationBuilder() - .bindings(new GuiceModule) + .bindings(GuiceableModule.guiceable(new GuiceModule)) .build() /** * The guice module. */ - class GuiceModule extends ScalaModule { + class GuiceModule extends AbstractModule with ScalaModule { override def configure(): Unit = { bind[Silhouette[UserAwareEnv]].to[SilhouetteProvider[UserAwareEnv]] bind[Environment[UserAwareEnv]].toInstance(env) From f25f37861677dcd6ef2bb235e381e59ac320e189 Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Tue, 28 Nov 2023 15:32:05 +0100 Subject: [PATCH 20/30] Add running wrapper for play specs with scala 3 --- .../play/silhouette/test/FakesSpec.scala | 168 ++--- .../providers/GoogleTotpProviderSpec.scala | 60 +- .../silhouette/api/ErrorHandlerSpec.scala | 216 ++++--- .../play/silhouette/api/EventBusSpec.scala | 162 ++--- .../api/actions/SecuredActionSpec.scala | 596 ++++++++++-------- .../api/actions/UnsecuredActionSpec.scala | 140 ++-- .../api/actions/UserAwareActionSpec.scala | 334 +++++----- .../services/AuthenticatorResultSpec.scala | 24 +- .../api/util/PlayHTTPLayerSpec.scala | 14 +- .../BearerTokenAuthenticatorSpec.scala | 28 +- .../CookieAuthenticatorSpec.scala | 186 +++--- .../DummyAuthenticatorSpec.scala | 8 +- .../authenticators/JWTAuthenticatorSpec.scala | 471 ++++++++------ .../SessionAuthenticatorSpec.scala | 374 ++++++----- .../providers/BasicAuthProviderSpec.scala | 134 ++-- .../providers/CredentialsProviderSpec.scala | 90 +-- .../impl/providers/OAuth1ProviderSpec.scala | 94 +-- .../impl/providers/OAuth2ProviderSpec.scala | 266 ++++---- .../impl/providers/OpenIDProviderSpec.scala | 68 +- .../custom/FacebookProviderSpec.scala | 162 ++--- .../oauth1/LinkedInProviderSpec.scala | 124 ++-- .../oauth1/TwitterProviderSpec.scala | 140 ++-- .../providers/oauth1/XingProviderSpec.scala | 118 ++-- .../oauth1/secrets/CookieSecretSpec.scala | 122 ++-- .../services/PlayOAuth1ServiceSpec.scala | 10 +- .../providers/oauth2/Auth0ProviderSpec.scala | 206 +++--- .../oauth2/DropboxProviderSpec.scala | 238 +++---- .../oauth2/FacebookProviderSpec.scala | 228 ++++--- .../oauth2/FoursquareProviderSpec.scala | 328 +++++----- .../providers/oauth2/GitHubProviderSpec.scala | 238 +++---- .../providers/oauth2/GitLabProviderSpec.scala | 220 ++++--- .../providers/oauth2/GoogleProviderSpec.scala | 352 ++++++----- .../oauth2/InstagramProviderSpec.scala | 222 ++++--- .../oauth2/LinkedInProviderSpec.scala | 238 +++---- .../providers/oauth2/VKProviderSpec.scala | 314 ++++----- .../providers/openid/SteamProviderSpec.scala | 22 +- .../providers/openid/YahooProviderSpec.scala | 30 +- .../service/PlayOpenIDServiceSpec.scala | 10 +- 38 files changed, 3751 insertions(+), 3004 deletions(-) diff --git a/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala b/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala index f95891b7..1555897c 100644 --- a/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala +++ b/silhouette-testkit/test/io/github/honeycombcheesecake/play/silhouette/test/FakesSpec.scala @@ -113,7 +113,9 @@ class FakesSpec extends PlaySpecification with JsonMatchers { } "return a `CookieAuthenticatorService`" in new WithApplication { - FakeAuthenticatorService[CookieAuthenticator]() must beAnInstanceOf[CookieAuthenticatorService] + override def running() = { + FakeAuthenticatorService[CookieAuthenticator]() must beAnInstanceOf[CookieAuthenticatorService] + } } "return a `BearerTokenAuthenticatorService`" in { @@ -131,92 +133,108 @@ class FakesSpec extends PlaySpecification with JsonMatchers { "The `FakeAuthenticator` factory" should { "return a `SessionAuthenticator`" in new WithApplication { - val loginInfo = LoginInfo("test", "test") - val identity = FakeIdentity(loginInfo) - implicit val env: FakeEnvironment[SessionEnv] = FakeEnvironment[SessionEnv](Seq(loginInfo -> identity)) - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + override def running() = { + val loginInfo = LoginInfo("test", "test") + val identity = FakeIdentity(loginInfo) + implicit val env: FakeEnvironment[SessionEnv] = FakeEnvironment[SessionEnv](Seq(loginInfo -> identity)) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - FakeAuthenticator(loginInfo) must beAnInstanceOf[SessionAuthenticator] + FakeAuthenticator(loginInfo) must beAnInstanceOf[SessionAuthenticator] + } } "return a `CookieAuthenticator`" in new WithApplication { - val loginInfo = LoginInfo("test", "test") - val identity = FakeIdentity(loginInfo) - implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + override def running() = { + val loginInfo = LoginInfo("test", "test") + val identity = FakeIdentity(loginInfo) + implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - FakeAuthenticator(loginInfo) must beAnInstanceOf[CookieAuthenticator] + FakeAuthenticator(loginInfo) must beAnInstanceOf[CookieAuthenticator] + } } "return a `BearerTokenAuthenticator`" in new WithApplication { - val loginInfo = LoginInfo("test", "test") - val identity = FakeIdentity(loginInfo) - implicit val env: FakeEnvironment[BearerTokenEnv] = FakeEnvironment[BearerTokenEnv](Seq(loginInfo -> identity)) - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + override def running() = { + val loginInfo = LoginInfo("test", "test") + val identity = FakeIdentity(loginInfo) + implicit val env: FakeEnvironment[BearerTokenEnv] = FakeEnvironment[BearerTokenEnv](Seq(loginInfo -> identity)) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - FakeAuthenticator(loginInfo) must beAnInstanceOf[BearerTokenAuthenticator] + FakeAuthenticator(loginInfo) must beAnInstanceOf[BearerTokenAuthenticator] + } } "return a `JWTAuthenticator`" in new WithApplication { - val loginInfo = LoginInfo("test", "test") - val identity = FakeIdentity(loginInfo) - implicit val env: FakeEnvironment[JWTEnv] = FakeEnvironment[JWTEnv](Seq(loginInfo -> identity)) - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + override def running() = { + val loginInfo = LoginInfo("test", "test") + val identity = FakeIdentity(loginInfo) + implicit val env: FakeEnvironment[JWTEnv] = FakeEnvironment[JWTEnv](Seq(loginInfo -> identity)) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - FakeAuthenticator(loginInfo) must beAnInstanceOf[JWTAuthenticator] + FakeAuthenticator(loginInfo) must beAnInstanceOf[JWTAuthenticator] + } } "return a `DummyAuthenticator`" in new WithApplication { - val loginInfo = LoginInfo("test", "test") - val identity = FakeIdentity(loginInfo) - implicit val env: FakeEnvironment[DummyEnv] = FakeEnvironment[DummyEnv](Seq(loginInfo -> identity)) - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + override def running() = { + val loginInfo = LoginInfo("test", "test") + val identity = FakeIdentity(loginInfo) + implicit val env: FakeEnvironment[DummyEnv] = FakeEnvironment[DummyEnv](Seq(loginInfo -> identity)) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - FakeAuthenticator(loginInfo) must beAnInstanceOf[DummyAuthenticator] + FakeAuthenticator(loginInfo) must beAnInstanceOf[DummyAuthenticator] + } } } "The `securedAction` method of the `SecuredController`" should { "return a 401 status code if no authenticator was found" in new InjectorContext { new WithApplication(app) { - val loginInfo = LoginInfo("test", "test") - val identity = FakeIdentity(loginInfo) - val env = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) - val request = FakeRequest() + override def running() = { + val loginInfo = LoginInfo("test", "test") + val identity = FakeIdentity(loginInfo) + val env = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) + val request = FakeRequest() - val controller = app.injector.instanceOf[SecuredController] - val result = controller.defaultSecuredAction(request) + val controller = app.injector.instanceOf[SecuredController] + val result = controller.defaultSecuredAction(request) - status(result) must equalTo(UNAUTHORIZED) + status(result) must equalTo(UNAUTHORIZED) + } } } "return a 401 status code if authenticator but no identity was found" in new InjectorContext { new WithApplication(app) { - val loginInfo = LoginInfo("test", "test") - val identity = FakeIdentity(loginInfo) - implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) - val request = FakeRequest().withAuthenticator(LoginInfo("invalid", "invalid")) + override def running() = { + val loginInfo = LoginInfo("test", "test") + val identity = FakeIdentity(loginInfo) + implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) + val request = FakeRequest().withAuthenticator(LoginInfo("invalid", "invalid")) - val controller = app.injector.instanceOf[SecuredController] - val result = controller.defaultSecuredAction(request) + val controller = app.injector.instanceOf[SecuredController] + val result = controller.defaultSecuredAction(request) - status(result) must equalTo(UNAUTHORIZED) + status(result) must equalTo(UNAUTHORIZED) + } } } "return a 200 status code if authenticator and identity was found" in new InjectorContext { new WithApplication(app) { - val loginInfo = LoginInfo("test", "test") - val identity = FakeIdentity(loginInfo) - implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) - val request = FakeRequest().withAuthenticator(loginInfo) - - val controller = app.injector.instanceOf[SecuredController] - val result = controller.defaultSecuredAction(request) - - status(result) must equalTo(OK) - contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "test") + override def running() = { + val loginInfo = LoginInfo("test", "test") + val identity = FakeIdentity(loginInfo) + implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) + val request = FakeRequest().withAuthenticator(loginInfo) + + val controller = app.injector.instanceOf[SecuredController] + val result = controller.defaultSecuredAction(request) + + status(result) must equalTo(OK) + contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "test") + } } } } @@ -224,45 +242,51 @@ class FakesSpec extends PlaySpecification with JsonMatchers { "The `userAwareAction` method of the `SecuredController`" should { "return a 401 status code if no authenticator was found" in new InjectorContext { new WithApplication(app) { - val loginInfo = LoginInfo("test", "test") - val identity = FakeIdentity(loginInfo) - val env = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) - val request = FakeRequest() + override def running() = { + val loginInfo = LoginInfo("test", "test") + val identity = FakeIdentity(loginInfo) + val env = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) + val request = FakeRequest() - val controller = app.injector.instanceOf[SecuredController] - val result = controller.defaultUserAwareAction(request) + val controller = app.injector.instanceOf[SecuredController] + val result = controller.defaultUserAwareAction(request) - status(result) must equalTo(UNAUTHORIZED) + status(result) must equalTo(UNAUTHORIZED) + } } } "return a 401 status code if authenticator but no identity was found" in new InjectorContext { new WithApplication(app) { - val loginInfo = LoginInfo("test", "test") - val identity = FakeIdentity(loginInfo) - implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) - val request = FakeRequest().withAuthenticator(LoginInfo("invalid", "invalid")) + override def running() = { + val loginInfo = LoginInfo("test", "test") + val identity = FakeIdentity(loginInfo) + implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) + val request = FakeRequest().withAuthenticator(LoginInfo("invalid", "invalid")) - val controller = app.injector.instanceOf[SecuredController] - val result = controller.defaultUserAwareAction(request) + val controller = app.injector.instanceOf[SecuredController] + val result = controller.defaultUserAwareAction(request) - status(result) must equalTo(UNAUTHORIZED) + status(result) must equalTo(UNAUTHORIZED) + } } } "return a 200 status code if authenticator and identity was found" in new InjectorContext { new WithApplication(app) { - val loginInfo = LoginInfo("test", "test") - val identity = FakeIdentity(loginInfo) + override def running() = { + val loginInfo = LoginInfo("test", "test") + val identity = FakeIdentity(loginInfo) - implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) - val request = FakeRequest().withAuthenticator(loginInfo) + implicit val env: FakeEnvironment[CookieEnv] = FakeEnvironment[CookieEnv](Seq(loginInfo -> identity)) + val request = FakeRequest().withAuthenticator(loginInfo) - val controller = app.injector.instanceOf[SecuredController] - val result = controller.defaultUserAwareAction(request) + val controller = app.injector.instanceOf[SecuredController] + val result = controller.defaultUserAwareAction(request) - status(result) must equalTo(OK) - contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "test") + status(result) must equalTo(OK) + contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "test") + } } } } diff --git a/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala b/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala index 65f626d9..39235959 100644 --- a/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala +++ b/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala @@ -29,55 +29,73 @@ import scala.concurrent.ExecutionContext.Implicits.global class GoogleTotpProviderSpec extends PasswordProviderSpec { "The `authenticate` with verification code method" should { "return None when the sharedKey is null or empty" in new WithApplication with Context { - await(provider.authenticate(null.asInstanceOf[String], testVerificationCode)) should be(None) - await(provider.authenticate("", testVerificationCode)) should be(None) + override def running() = { + await(provider.authenticate(null.asInstanceOf[String], testVerificationCode)) should be(None) + await(provider.authenticate("", testVerificationCode)) should be(None) + } } "return None when the verification code is null or empty" in new WithApplication with Context { - await(provider.authenticate(testSharedKey, null)) should be(None) - await(provider.authenticate(testSharedKey, "")) should be(None) + override def running() = { + await(provider.authenticate(testSharedKey, null)) should be(None) + await(provider.authenticate(testSharedKey, "")) should be(None) + } } "return None when the verification code isn't a number" in new WithApplication with Context { - await(provider.authenticate(testSharedKey, testWrongVerificationCode)) should be(None) + override def running() = { + await(provider.authenticate(testSharedKey, testWrongVerificationCode)) should be(None) + } } "return valid `Some(TotpInfo)` when the verification code is correct" in new WithApplication with Context { - val googleAuthenticator = new GoogleAuthenticator() - val validVerificationCode = googleAuthenticator.getTotpPassword(testSharedKey) - await(provider.authenticate(testSharedKey, validVerificationCode.toString)) should not be empty + override def running() = { + val googleAuthenticator = new GoogleAuthenticator() + val validVerificationCode = googleAuthenticator.getTotpPassword(testSharedKey) + await(provider.authenticate(testSharedKey, validVerificationCode.toString)) should not be empty + } } } "The `createCredentials` method" should { "return the correct TotpCredentials shared key" in new WithApplication with Context { - val result = provider.createCredentials(credentials.identifier) - result.totpInfo.sharedKey.nonEmpty must beTrue - result.totpInfo.scratchCodes.nonEmpty must beTrue - result.qrUrl.nonEmpty must beTrue + override def running() = { + val result = provider.createCredentials(credentials.identifier) + result.totpInfo.sharedKey.nonEmpty must beTrue + result.totpInfo.scratchCodes.nonEmpty must beTrue + result.qrUrl.nonEmpty must beTrue + } } } "The `authenticate` with verification code method" should { "throw NullPointerException when the input totpInfo is null" in new WithApplication with Context { - await(provider.authenticate(null.asInstanceOf[GoogleTotpInfo], testWrongVerificationCode)) must throwA[NullPointerException] + override def running() = { + await(provider.authenticate(null.asInstanceOf[GoogleTotpInfo], testWrongVerificationCode)) must throwA[NullPointerException] + } } "return throw NullPointerException when the plain scratch code is null" in new WithApplication with Context { - val result = provider.createCredentials(credentials.identifier) - await(provider.authenticate(result.totpInfo, null.asInstanceOf[String])) must throwA[NullPointerException] + override def running() = { + val result = provider.createCredentials(credentials.identifier) + await(provider.authenticate(result.totpInfo, null.asInstanceOf[String])) must throwA[NullPointerException] + } } "return None when the plain scratch code is empty" in new WithApplication with Context { - val result = provider.createCredentials(credentials.identifier) - await(provider.authenticate(result.totpInfo, "")) should be(None) + override def running() = { + val result = provider.createCredentials(credentials.identifier) + await(provider.authenticate(result.totpInfo, "")) should be(None) + } } "return Some(PasswordInfo,TotpInfo) when the plain scratch code is valid" in new WithApplication with Context { - when(fooHasher.hash(any())).thenReturn(testPasswordInfo) - when(barHasher.matches(testPasswordInfo, testScratchCode)).thenReturn(true) - val result = provider.createCredentials(credentials.identifier) - await(provider.authenticate(result.totpInfo, testScratchCode)) should not be empty + override def running() = { + when(fooHasher.hash(any())).thenReturn(testPasswordInfo) + when(barHasher.matches(testPasswordInfo, testScratchCode)).thenReturn(true) + val result = provider.createCredentials(credentials.identifier) + await(provider.authenticate(result.totpInfo, testScratchCode)) should not be empty + } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala index d72eb0c6..f90b48d1 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/ErrorHandlerSpec.scala @@ -30,125 +30,173 @@ class ErrorHandlerSpec extends PlaySpecification { "The `DefaultNotAuthenticatedErrorHandler.notAuthenticated` method" should { "return an HTML response for an HTML request" in new WithApplication with Context { - testResponse( - acceptedMediaType = Some(HTML), - expectedStatus = UNAUTHORIZED, - expectedContentType = HTML, - expectedResponseFragment = "", - expectedMessage = "silhouette.not.authenticated", - f = { notAuthenticated.onNotAuthenticated(_: RequestHeader) }) + override def running() = { + testResponse( + acceptedMediaType = Some(HTML), + expectedStatus = UNAUTHORIZED, + expectedContentType = HTML, + expectedResponseFragment = "", + expectedMessage = "silhouette.not.authenticated", + f = { + notAuthenticated.onNotAuthenticated(_: RequestHeader) + }) + } } "return a JSON response for a JSON request" in new WithApplication with Context { - testResponse( - acceptedMediaType = Some(JSON), - expectedStatus = UNAUTHORIZED, - expectedContentType = JSON, - expectedResponseFragment = "\"success\":false", - expectedMessage = "silhouette.not.authenticated", - f = { notAuthenticated.onNotAuthenticated(_: RequestHeader) }) + override def running() = { + testResponse( + acceptedMediaType = Some(JSON), + expectedStatus = UNAUTHORIZED, + expectedContentType = JSON, + expectedResponseFragment = "\"success\":false", + expectedMessage = "silhouette.not.authenticated", + f = { + notAuthenticated.onNotAuthenticated(_: RequestHeader) + }) + } } "return a XML response for a XML request" in new WithApplication with Context { - testResponse( - acceptedMediaType = Some(XML), - expectedStatus = UNAUTHORIZED, - expectedContentType = XML, - expectedResponseFragment = "false", - expectedMessage = "silhouette.not.authenticated", - f = { notAuthenticated.onNotAuthenticated(_: RequestHeader) }) + override def running() = { + testResponse( + acceptedMediaType = Some(XML), + expectedStatus = UNAUTHORIZED, + expectedContentType = XML, + expectedResponseFragment = "false", + expectedMessage = "silhouette.not.authenticated", + f = { + notAuthenticated.onNotAuthenticated(_: RequestHeader) + }) + } } "return a plain text response for a plain text request" in new WithApplication with Context { - testResponse( - acceptedMediaType = Some(TEXT), - expectedStatus = UNAUTHORIZED, - expectedContentType = TEXT, - expectedResponseFragment = messagesApi("silhouette.not.authenticated"), - expectedMessage = "silhouette.not.authenticated", - f = { notAuthenticated.onNotAuthenticated(_: RequestHeader) }) + override def running() = { + testResponse( + acceptedMediaType = Some(TEXT), + expectedStatus = UNAUTHORIZED, + expectedContentType = TEXT, + expectedResponseFragment = messagesApi("silhouette.not.authenticated"), + expectedMessage = "silhouette.not.authenticated", + f = { + notAuthenticated.onNotAuthenticated(_: RequestHeader) + }) + } } "return a plain text response for other requests" in new WithApplication with Context { - testResponse( - acceptedMediaType = Some(BINARY), - expectedStatus = UNAUTHORIZED, - expectedContentType = TEXT, - expectedResponseFragment = messagesApi("silhouette.not.authenticated"), - expectedMessage = "silhouette.not.authenticated", - f = { notAuthenticated.onNotAuthenticated(_: RequestHeader) }) + override def running() = { + testResponse( + acceptedMediaType = Some(BINARY), + expectedStatus = UNAUTHORIZED, + expectedContentType = TEXT, + expectedResponseFragment = messagesApi("silhouette.not.authenticated"), + expectedMessage = "silhouette.not.authenticated", + f = { + notAuthenticated.onNotAuthenticated(_: RequestHeader) + }) + } } "return an HTML response for a request without an Accept header" in new WithApplication with Context { - testResponse( - acceptedMediaType = None, - expectedStatus = UNAUTHORIZED, - expectedContentType = HTML, - expectedResponseFragment = messagesApi("silhouette.not.authenticated"), - expectedMessage = "silhouette.not.authenticated", - f = { notAuthenticated.onNotAuthenticated(_: RequestHeader) }) + override def running() = { + testResponse( + acceptedMediaType = None, + expectedStatus = UNAUTHORIZED, + expectedContentType = HTML, + expectedResponseFragment = messagesApi("silhouette.not.authenticated"), + expectedMessage = "silhouette.not.authenticated", + f = { + notAuthenticated.onNotAuthenticated(_: RequestHeader) + }) + } } } "The `DefaultNotAuthorizedErrorHandler.onNotAuthorized` method" should { "return an HTML response for an HTML request" in new WithApplication with Context { - testResponse( - acceptedMediaType = Some(HTML), - expectedStatus = FORBIDDEN, - expectedContentType = HTML, - expectedResponseFragment = "", - expectedMessage = "silhouette.not.authorized", - f = { notAuthorized.onNotAuthorized(_: RequestHeader) }) + override def running() = { + testResponse( + acceptedMediaType = Some(HTML), + expectedStatus = FORBIDDEN, + expectedContentType = HTML, + expectedResponseFragment = "", + expectedMessage = "silhouette.not.authorized", + f = { + notAuthorized.onNotAuthorized(_: RequestHeader) + }) + } } "return a JSON response for a JSON request" in new WithApplication with Context { - testResponse( - acceptedMediaType = Some(JSON), - expectedStatus = FORBIDDEN, - expectedContentType = JSON, - expectedResponseFragment = "\"success\":false", - expectedMessage = "silhouette.not.authorized", - f = { notAuthorized.onNotAuthorized(_: RequestHeader) }) + override def running() = { + testResponse( + acceptedMediaType = Some(JSON), + expectedStatus = FORBIDDEN, + expectedContentType = JSON, + expectedResponseFragment = "\"success\":false", + expectedMessage = "silhouette.not.authorized", + f = { + notAuthorized.onNotAuthorized(_: RequestHeader) + }) + } } "return a XML response for a XML request" in new WithApplication with Context { - testResponse( - acceptedMediaType = Some(XML), - expectedStatus = FORBIDDEN, - expectedContentType = XML, - expectedResponseFragment = "false", - expectedMessage = "silhouette.not.authorized", - f = { notAuthorized.onNotAuthorized(_: RequestHeader) }) + override def running() = { + testResponse( + acceptedMediaType = Some(XML), + expectedStatus = FORBIDDEN, + expectedContentType = XML, + expectedResponseFragment = "false", + expectedMessage = "silhouette.not.authorized", + f = { + notAuthorized.onNotAuthorized(_: RequestHeader) + }) + } } "return a plain text response for a plain text request" in new WithApplication with Context { - testResponse( - acceptedMediaType = Some(TEXT), - expectedStatus = FORBIDDEN, - expectedContentType = TEXT, - expectedResponseFragment = messagesApi("silhouette.not.authorized"), - expectedMessage = "silhouette.not.authorized", - f = { notAuthorized.onNotAuthorized(_: RequestHeader) }) + override def running() = { + testResponse( + acceptedMediaType = Some(TEXT), + expectedStatus = FORBIDDEN, + expectedContentType = TEXT, + expectedResponseFragment = messagesApi("silhouette.not.authorized"), + expectedMessage = "silhouette.not.authorized", + f = { + notAuthorized.onNotAuthorized(_: RequestHeader) + }) + } } "return a plain text response for other requests" in new WithApplication with Context { - testResponse( - acceptedMediaType = Some(BINARY), - expectedStatus = FORBIDDEN, - expectedContentType = TEXT, - expectedResponseFragment = messagesApi("silhouette.not.authorized"), - expectedMessage = "silhouette.not.authorized", - f = { notAuthorized.onNotAuthorized(_: RequestHeader) }) + override def running() = { + testResponse( + acceptedMediaType = Some(BINARY), + expectedStatus = FORBIDDEN, + expectedContentType = TEXT, + expectedResponseFragment = messagesApi("silhouette.not.authorized"), + expectedMessage = "silhouette.not.authorized", + f = { + notAuthorized.onNotAuthorized(_: RequestHeader) + }) + } } "return an HTML response for a request without an Accept header" in new WithApplication with Context { - testResponse( - acceptedMediaType = None, - expectedStatus = FORBIDDEN, - expectedContentType = HTML, - expectedResponseFragment = messagesApi("silhouette.not.authorized"), - expectedMessage = "silhouette.not.authorized", - f = { notAuthorized.onNotAuthorized(_: RequestHeader) }) + override def running() = { + testResponse( + acceptedMediaType = None, + expectedStatus = FORBIDDEN, + expectedContentType = HTML, + expectedResponseFragment = messagesApi("silhouette.not.authorized"), + expectedMessage = "silhouette.not.authorized", + f = { + notAuthorized.onNotAuthorized(_: RequestHeader) + }) + } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/EventBusSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/EventBusSpec.scala index 491f9bca..a5e244e3 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/EventBusSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/EventBusSpec.scala @@ -30,102 +30,116 @@ class EventBusSpec extends PlaySpecification { "The event bus" should { "handle an subclass event" in new WithApplication with Context { - val eventBus = new EventBus - val listener = system.actorOf(Props(new Actor { - def receive = { - case e => theProbe.ref ! e - } - })) - - eventBus.subscribe(listener, classOf[SilhouetteEvent]) - - eventBus.publish(loginEvent) - theProbe.expectMsg(500 millis, loginEvent) - - eventBus.publish(logoutEvent) - theProbe.expectMsg(500 millis, logoutEvent) + override def running() = { + val eventBus = new EventBus + val listener = system.actorOf(Props(new Actor { + def receive = { + case e => theProbe.ref ! e + } + })) + + eventBus.subscribe(listener, classOf[SilhouetteEvent]) + + eventBus.publish(loginEvent) + theProbe.expectMsg(500 millis, loginEvent) + + eventBus.publish(logoutEvent) + theProbe.expectMsg(500 millis, logoutEvent) + } } "handle an event" in new WithApplication with Context { - val eventBus = new EventBus - val listener = system.actorOf(Props(new Actor { - def receive = { - case e @ LoginEvent(_, _) => theProbe.ref ! e - } - })) - - eventBus.subscribe(listener, classOf[LoginEvent[TestIdentity]]) - eventBus.publish(loginEvent) - - theProbe.expectMsg(500 millis, loginEvent) + override def running() = { + val eventBus = new EventBus + val listener = system.actorOf(Props(new Actor { + def receive = { + case e@LoginEvent(_, _) => theProbe.ref ! e + } + })) + + eventBus.subscribe(listener, classOf[LoginEvent[TestIdentity]]) + eventBus.publish(loginEvent) + + theProbe.expectMsg(500 millis, loginEvent) + } } "handle multiple events" in new WithApplication with Context { - val eventBus = new EventBus - val listener = system.actorOf(Props(new Actor { - def receive = { - case e @ LoginEvent(_, _) => theProbe.ref ! e - case e @ LogoutEvent(_, _) => theProbe.ref ! e - } - })) - - eventBus.subscribe(listener, classOf[LoginEvent[TestIdentity]]) - eventBus.subscribe(listener, classOf[LogoutEvent[TestIdentity]]) - eventBus.publish(loginEvent) - eventBus.publish(logoutEvent) - - theProbe.expectMsg(500 millis, loginEvent) - theProbe.expectMsg(500 millis, logoutEvent) + override def running() = { + val eventBus = new EventBus + val listener = system.actorOf(Props(new Actor { + def receive = { + case e@LoginEvent(_, _) => theProbe.ref ! e + case e@LogoutEvent(_, _) => theProbe.ref ! e + } + })) + + eventBus.subscribe(listener, classOf[LoginEvent[TestIdentity]]) + eventBus.subscribe(listener, classOf[LogoutEvent[TestIdentity]]) + eventBus.publish(loginEvent) + eventBus.publish(logoutEvent) + + theProbe.expectMsg(500 millis, loginEvent) + theProbe.expectMsg(500 millis, logoutEvent) + } } "differentiate between event classes" in new WithApplication with Context { - val eventBus = new EventBus - val listener = system.actorOf(Props(new Actor { - def receive = { - case e @ LoginEvent(_, _) => theProbe.ref ! e - } - })) - - eventBus.subscribe(listener, classOf[LogoutEvent[TestIdentity]]) - eventBus.publish(logoutEvent) - - theProbe.expectNoMessage(500 millis) + override def running() = { + val eventBus = new EventBus + val listener = system.actorOf(Props(new Actor { + def receive = { + case e@LoginEvent(_, _) => theProbe.ref ! e + } + })) + + eventBus.subscribe(listener, classOf[LogoutEvent[TestIdentity]]) + eventBus.publish(logoutEvent) + + theProbe.expectNoMessage(500 millis) + } } "not handle not subscribed events" in new WithApplication with Context { - val eventBus = new EventBus - val listener = system.actorOf(Props(new Actor { - def receive = { - case e @ LoginEvent(_, _) => theProbe.ref ! e - } - })) - - eventBus.publish(loginEvent) - - theProbe.expectNoMessage(500 millis) + override def running() = { + val eventBus = new EventBus + val listener = system.actorOf(Props(new Actor { + def receive = { + case e@LoginEvent(_, _) => theProbe.ref ! e + } + })) + + eventBus.publish(loginEvent) + + theProbe.expectNoMessage(500 millis) + } } "not handle events between different event buses" in new WithApplication with Context { - val eventBus1 = new EventBus - val eventBus2 = new EventBus + override def running() = { + val eventBus1 = new EventBus + val eventBus2 = new EventBus - val listener = system.actorOf(Props(new Actor { - def receive = { - case e @ LoginEvent(_, _) => theProbe.ref ! e - } - })) + val listener = system.actorOf(Props(new Actor { + def receive = { + case e@LoginEvent(_, _) => theProbe.ref ! e + } + })) - eventBus1.subscribe(listener, classOf[LoginEvent[TestIdentity]]) - eventBus2.publish(loginEvent) + eventBus1.subscribe(listener, classOf[LoginEvent[TestIdentity]]) + eventBus2.publish(loginEvent) - theProbe.expectNoMessage(500 millis) + theProbe.expectNoMessage(500 millis) + } } "returns a singleton event bus" in new WithApplication with Context { - val eventBus1 = EventBus() - val eventBus2 = EventBus() + override def running() = { + val eventBus1 = EventBus() + val eventBus2 = EventBus() - eventBus1 ==== eventBus2 + eventBus1 ==== eventBus2 + } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala index 87d944ce..a55d5640 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredActionSpec.scala @@ -53,398 +53,438 @@ class SecuredActionSpec extends PlaySpecification with JsonMatchers { "The `SecuredAction` action" should { "restrict access if no valid authenticator can be retrieved" in new InjectorContext { new WithApplication(app) with Context { - withEvent[NotAuthenticatedEvent] { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + override def running() = { + withEvent[NotAuthenticatedEvent] { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - val result = controller.defaultAction(request) + val result = controller.defaultAction(request) - status(result) must equalTo(UNAUTHORIZED) - contentAsString(result) must contain("global.not.authenticated") - theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request)) + status(result) must equalTo(UNAUTHORIZED) + contentAsString(result) must contain("global.not.authenticated") + theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request)) + } } } } "restrict access and discard authenticator if an invalid authenticator can be retrieved" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator.copy(isValid = false)))) - when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator.copy(isValid = false)))) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } - withEvent[NotAuthenticatedEvent] { - val result = controller.defaultAction(request) + withEvent[NotAuthenticatedEvent] { + val result = controller.defaultAction(request) - status(result) must equalTo(UNAUTHORIZED) - contentAsString(result) must contain("global.not.authenticated") - verify(env.authenticatorService).discard(any(), any())(any()) - theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request)) + status(result) must equalTo(UNAUTHORIZED) + contentAsString(result) must contain("global.not.authenticated") + verify(env.authenticatorService).discard(any(), any())(any()) + theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request)) + } } } } "restrict access and discard authenticator if no identity could be found for an authenticator" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) - withEvent[NotAuthenticatedEvent] { - val result = controller.defaultAction(request) + withEvent[NotAuthenticatedEvent] { + val result = controller.defaultAction(request) - status(result) must equalTo(UNAUTHORIZED) - contentAsString(result) must contain("global.not.authenticated") - verify(env.authenticatorService).discard(any(), any())(any()) - theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request)) + status(result) must equalTo(UNAUTHORIZED) + contentAsString(result) must contain("global.not.authenticated") + verify(env.authenticatorService).discard(any(), any())(any()) + theProbe.expectMsg(500 millis, NotAuthenticatedEvent(request)) + } } } } "display local not-authenticated result if user isn't authenticated[authorization and error handler]" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) - val result = controller.actionWithAuthorizationAndErrorHandler(request) + val result = controller.actionWithAuthorizationAndErrorHandler(request) - status(result) must equalTo(UNAUTHORIZED) - contentAsString(result) must contain("local.not.authenticated") + status(result) must equalTo(UNAUTHORIZED) + contentAsString(result) must contain("local.not.authenticated") + } } } "display local not-authenticated result if user isn't authenticated[error handler only]" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) - val result = controller.actionWithErrorHandler(request) + val result = controller.actionWithErrorHandler(request) - status(result) must equalTo(UNAUTHORIZED) - contentAsString(result) must contain("local.not.authenticated") + status(result) must equalTo(UNAUTHORIZED) + contentAsString(result) must contain("local.not.authenticated") + } } } "display global not-authenticated result if user isn't authenticated" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) - val result = controller.defaultAction(request) + val result = controller.defaultAction(request) - status(result) must equalTo(UNAUTHORIZED) - contentAsString(result) must contain("global.not.authenticated") + status(result) must equalTo(UNAUTHORIZED) + contentAsString(result) must contain("global.not.authenticated") + } } } "restrict access and update authenticator if a user is authenticated but not authorized" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - when(authorization.isAuthorized(any(), any())(any())).thenReturn(Future.successful(false)) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + when(authorization.isAuthorized(any(), any())(any())).thenReturn(Future.successful(false)) - withEvent[NotAuthorizedEvent[FakeIdentity]] { - val result = controller.actionWithAuthorization(request) + withEvent[NotAuthorizedEvent[FakeIdentity]] { + val result = controller.actionWithAuthorization(request) - status(result) must equalTo(FORBIDDEN) - contentAsString(result) must contain("global.not.authorized") - verify(env.authenticatorService).update(any(), any())(any()) - theProbe.expectMsg(500 millis, NotAuthorizedEvent(identity, request)) + status(result) must equalTo(FORBIDDEN) + contentAsString(result) must contain("global.not.authorized") + verify(env.authenticatorService).update(any(), any())(any()) + theProbe.expectMsg(500 millis, NotAuthorizedEvent(identity, request)) + } } } } "display local not-authorized result if user isn't authorized" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - when(authorization.isAuthorized(any(), any())(any())).thenReturn(Future.successful(false)) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + when(authorization.isAuthorized(any(), any())(any())).thenReturn(Future.successful(false)) - val result = controller.actionWithAuthorizationAndErrorHandler(request) + val result = controller.actionWithAuthorizationAndErrorHandler(request) - status(result) must equalTo(FORBIDDEN) - contentAsString(result) must contain("local.not.authorized") - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).update(any(), any())(any()) + status(result) must equalTo(FORBIDDEN) + contentAsString(result) must contain("local.not.authorized") + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) + } } } "display global not-authorized result if user isn't authorized" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - when(authorization.isAuthorized(any(), any())(any())).thenReturn(Future.successful(false)) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + when(authorization.isAuthorized(any(), any())(any())).thenReturn(Future.successful(false)) - val result = controller.actionWithAuthorization(request) + val result = controller.actionWithAuthorization(request) - status(result) must equalTo(FORBIDDEN) - contentAsString(result) must contain("global.not.authorized") - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).update(any(), any())(any()) + status(result) must equalTo(FORBIDDEN) + contentAsString(result) must contain("global.not.authorized") + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) + } } } "invoke action without authorization if user is authenticated" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - withEvent[AuthenticatedEvent[FakeIdentity]] { - val result = controller.defaultAction(request) + withEvent[AuthenticatedEvent[FakeIdentity]] { + val result = controller.defaultAction(request) - status(result) must equalTo(OK) - contentAsString(result) must contain("full.access") - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).update(any(), any())(any()) - theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + status(result) must equalTo(OK) + contentAsString(result) must contain("full.access") + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) + theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + } } } } "invoke action with authorization if user is authenticated but not authorized" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - withEvent[AuthenticatedEvent[FakeIdentity]] { - val result = controller.actionWithAuthorization(request) + withEvent[AuthenticatedEvent[FakeIdentity]] { + val result = controller.actionWithAuthorization(request) - status(result) must equalTo(OK) - contentAsString(result) must contain("full.access") - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).update(any(), any())(any()) - theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + status(result) must equalTo(OK) + contentAsString(result) must contain("full.access") + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) + theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + } } } } "use next request provider in the chain if first isn't responsible" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(None)) - when(basicAuthRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) - when(env.authenticatorService.init(any())(any[RequestHeader]())).thenAnswer { p => - Future.successful(p.getArgument(0).asInstanceOf[FakeAuthenticator#Value]) - } - when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(None)) + when(basicAuthRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.init(any())(any[RequestHeader]())).thenAnswer { p => + Future.successful(p.getArgument(0).asInstanceOf[FakeAuthenticator#Value]) + } + when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - withEvent[AuthenticatedEvent[FakeIdentity]] { - val result = controller.actionWithAuthorization(request) + withEvent[AuthenticatedEvent[FakeIdentity]] { + val result = controller.actionWithAuthorization(request) - status(result) must equalTo(OK) - contentAsString(result) must contain("full.access") - verify(env.authenticatorService).create(any())(any()) - verify(env.authenticatorService).init(any())(any()) - theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + status(result) must equalTo(OK) + contentAsString(result) must contain("full.access") + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).init(any())(any()) + theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + } } } } "update an initialized authenticator if it was touched" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } - withEvent[AuthenticatedEvent[FakeIdentity]] { - val result = controller.actionWithAuthorization(request) + withEvent[AuthenticatedEvent[FakeIdentity]] { + val result = controller.actionWithAuthorization(request) - status(result) must equalTo(OK) - contentAsString(result) must contain("full.access") - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).update(any(), any())(any()) - theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + status(result) must equalTo(OK) + contentAsString(result) must contain("full.access") + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) + theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + } } } } "do not update an initialized authenticator if it was not touched" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Right(authenticator)) - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - - withEvent[AuthenticatedEvent[FakeIdentity]] { - val result = controller.actionWithAuthorization(request) - - status(result) must equalTo(OK) - contentAsString(result) must contain("full.access") - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService, never()).update(any(), any())(any()) - theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Right(authenticator)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + + withEvent[AuthenticatedEvent[FakeIdentity]] { + val result = controller.actionWithAuthorization(request) + + status(result) must equalTo(OK) + contentAsString(result) must contain("full.access") + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) + theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + } } } } "init an uninitialized authenticator" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) - when(env.authenticatorService.init(any())(any[RequestHeader]())).thenAnswer { p => - Future.successful(p.getArgument(0).asInstanceOf[FakeAuthenticator#Value]) - } - when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.init(any())(any[RequestHeader]())).thenAnswer { p => + Future.successful(p.getArgument(0).asInstanceOf[FakeAuthenticator#Value]) + } + when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - withEvent[AuthenticatedEvent[FakeIdentity]] { - val result = controller.actionWithAuthorization(request) + withEvent[AuthenticatedEvent[FakeIdentity]] { + val result = controller.actionWithAuthorization(request) - status(result) must equalTo(OK) - contentAsString(result) must contain("full.access") - verify(env.authenticatorService).create(any())(any()) - verify(env.authenticatorService).init(any())(any()) - theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + status(result) must equalTo(OK) + contentAsString(result) must contain("full.access") + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).init(any())(any()) + theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + } } } } "renew an initialized authenticator" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - withEvent[AuthenticatedEvent[FakeIdentity]] { - val result = controller.renewAction(request) + withEvent[AuthenticatedEvent[FakeIdentity]] { + val result = controller.renewAction(request) - status(result) must equalTo(OK) - contentAsString(result) must contain("renewed") - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).renew(any(), any())(any()) - verify(env.authenticatorService, never()).update(any(), any())(any()) - theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + status(result) must equalTo(OK) + contentAsString(result) must contain("renewed") + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).renew(any(), any())(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) + theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + } } } } "renew an uninitialized authenticator" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) - when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - withEvent[AuthenticatedEvent[FakeIdentity]] { - val result = controller.renewAction(request) + withEvent[AuthenticatedEvent[FakeIdentity]] { + val result = controller.renewAction(request) - status(result) must equalTo(OK) - contentAsString(result) must contain("renewed") - verify(env.authenticatorService).create(any())(any()) - verify(env.authenticatorService).renew(any(), any())(any()) - theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + status(result) must equalTo(OK) + contentAsString(result) must contain("renewed") + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).renew(any(), any())(any()) + theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + } } } } "discard an initialized authenticator" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - withEvent[AuthenticatedEvent[FakeIdentity]] { - val result = controller.discardAction(request) + withEvent[AuthenticatedEvent[FakeIdentity]] { + val result = controller.discardAction(request) - status(result) must equalTo(OK) - contentAsString(result) must contain("discarded") - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).discard(any(), any())(any()) - verify(env.authenticatorService, never()).update(any(), any())(any()) - theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + status(result) must equalTo(OK) + contentAsString(result) must contain("discarded") + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).discard(any(), any())(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) + theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + } } } } "discard an uninitialized authenticator" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) - when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - withEvent[AuthenticatedEvent[FakeIdentity]] { - val result = controller.discardAction(request) + withEvent[AuthenticatedEvent[FakeIdentity]] { + val result = controller.discardAction(request) - status(result) must equalTo(OK) - verify(env.authenticatorService).create(any())(any()) - verify(env.authenticatorService).discard(any(), any())(any()) - theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + status(result) must equalTo(OK) + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).discard(any(), any())(any()) + theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, request)) + } } } } "handle an Ajax request" in new InjectorContext { new WithApplication(app) with Context { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders("Accept" -> "application/json") + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders("Accept" -> "application/json") - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - withEvent[AuthenticatedEvent[FakeIdentity]] { - val result = controller.defaultAction(req) + withEvent[AuthenticatedEvent[FakeIdentity]] { + val result = controller.defaultAction(req) - status(result) must equalTo(OK) - contentType(result) must beSome("application/json") - contentAsString(result) must /("result" -> "full.access") - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).update(any(), any())(any()) - theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, req)) + status(result) must equalTo(OK) + contentType(result) must beSome("application/json") + contentAsString(result) must /("result" -> "full.access") + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) + theProbe.expectMsg(500 millis, AuthenticatedEvent(identity, req)) + } } } } @@ -453,31 +493,35 @@ class SecuredActionSpec extends PlaySpecification with JsonMatchers { "The `SecuredRequestHandler`" should { "return status 401 if authentication was not successful" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - val result = controller.defaultHandler(request) + val result = controller.defaultHandler(request) - status(result) must equalTo(UNAUTHORIZED) - verify(env.authenticatorService, never()).touch(any()) - verify(env.authenticatorService, never()).update(any(), any())(any()) + status(result) must equalTo(UNAUTHORIZED) + verify(env.authenticatorService, never()).touch(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) + } } } "return the user if authentication was successful" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - val result = controller.defaultHandler(request) + val result = controller.defaultHandler(request) - status(result) must equalTo(OK) - contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "1") - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).update(any(), any())(any()) + status(result) must equalTo(OK) + contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "1") + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) + } } } } @@ -485,29 +529,33 @@ class SecuredActionSpec extends PlaySpecification with JsonMatchers { "The `exceptionHandler` method of the SecuredErrorHandler" should { "translate an ForbiddenException into a 403 Forbidden result" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } - val failed = Future.failed(new NotAuthorizedException("Access denied")) - val result = controller.recover(failed) + val failed = Future.failed(new NotAuthorizedException("Access denied")) + val result = controller.recover(failed) - status(result) must equalTo(FORBIDDEN) + status(result) must equalTo(FORBIDDEN) + } } } "translate an UnauthorizedException into a 401 Unauthorized result" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } - val failed = Future.failed(new NotAuthenticatedException("Not authenticated")) - val result = controller.recover(failed) + val failed = Future.failed(new NotAuthenticatedException("Not authenticated")) + val result = controller.recover(failed) - status(result) must equalTo(UNAUTHORIZED) + status(result) must equalTo(UNAUTHORIZED) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala index 8ee05b63..89078a97 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UnsecuredActionSpec.scala @@ -50,75 +50,85 @@ class UnsecuredActionSpec extends PlaySpecification with JsonMatchers { "The `UnsecuredAction` action" should { "grant access if no valid authenticator can be retrieved" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(None)) + override def running() = { + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(None)) - val result = controller.defaultAction(request) + val result = controller.defaultAction(request) - status(result) must equalTo(OK) - contentAsString(result) must contain("full.access") + status(result) must equalTo(OK) + contentAsString(result) must contain("full.access") + } } } "grant access and discard authenticator if an invalid authenticator can be retrieved" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator.copy(isValid = false)))) - when(env.authenticatorService.discard(any, any)(any)).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } + override def running() = { + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator.copy(isValid = false)))) + when(env.authenticatorService.discard(any, any)(any)).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } - val result = controller.defaultAction(request) + val result = controller.defaultAction(request) - status(result) must equalTo(OK) - contentAsString(result) must contain("full.access") - verify(env.authenticatorService).discard(any, any)(any) + status(result) must equalTo(OK) + contentAsString(result) must contain("full.access") + verify(env.authenticatorService).discard(any, any)(any) + } } } "grant access and discard authenticator if no identity could be found for an authenticator" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.discard(any, any)(any)).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) + override def running() = { + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.discard(any, any)(any)).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) - val result = controller.defaultAction(request) + val result = controller.defaultAction(request) - status(result) must equalTo(OK) - contentAsString(result) must contain("full.access") - verify(env.authenticatorService).discard(any, any)(any) + status(result) must equalTo(OK) + contentAsString(result) must contain("full.access") + verify(env.authenticatorService).discard(any, any)(any) + } } } "display local not-authorized result if user is authenticated" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any)).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any, any)(any)).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any)).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any, any)(any)).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - val result = controller.actionWithErrorHandler(request) + val result = controller.actionWithErrorHandler(request) - status(result) must equalTo(FORBIDDEN) - contentAsString(result) must contain("local.not.authorized") + status(result) must equalTo(FORBIDDEN) + contentAsString(result) must contain("local.not.authorized") + } } } "display global not-authorized result if user is authenticated" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any)).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any, any)(any)).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any)).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any, any)(any)).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - val result = controller.defaultAction(request) + val result = controller.defaultAction(request) - status(result) must equalTo(FORBIDDEN) - contentAsString(result) must contain("global.not.authorized") + status(result) must equalTo(FORBIDDEN) + contentAsString(result) must contain("global.not.authorized") + } } } } @@ -126,31 +136,35 @@ class UnsecuredActionSpec extends PlaySpecification with JsonMatchers { "The `UnsecuredRequestHandler`" should { "return status 403 if user is authenticated" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any)).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any, any)(any)).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any)).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any, any)(any)).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - val result = controller.defaultHandler(request) + val result = controller.defaultHandler(request) - status(result) must equalTo(FORBIDDEN) - verify(env.authenticatorService).touch(any) - verify(env.authenticatorService).update(any, any)(any) + status(result) must equalTo(FORBIDDEN) + verify(env.authenticatorService).touch(any) + verify(env.authenticatorService).update(any, any)(any) + } } } "return the data if user is not authenticated" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(None)) + override def running() = { + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(None)) - val result = controller.defaultHandler(request) + val result = controller.defaultHandler(request) - status(result) must equalTo(OK) - contentAsString(result) must be equalTo "data" - verify(env.authenticatorService, never()).touch(any) - verify(env.authenticatorService, never()).update(any, any)(any) + status(result) must equalTo(OK) + contentAsString(result) must be equalTo "data" + verify(env.authenticatorService, never()).touch(any) + verify(env.authenticatorService, never()).update(any, any)(any) + } } } } @@ -158,15 +172,17 @@ class UnsecuredActionSpec extends PlaySpecification with JsonMatchers { "The `exceptionHandler` method of the UnsecuredErrorHandler" should { "translate an ForbiddenException into a 403 Forbidden result" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(None)) - when(env.authenticatorService.discard(any, any)(any)).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } + override def running() = { + when(env.authenticatorService.retrieve(any)).thenReturn(Future.successful(None)) + when(env.authenticatorService.discard(any, any)(any)).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } - val failed = Future.failed(new NotAuthorizedException("Access denied")) - val result = controller.recover(failed) + val failed = Future.failed(new NotAuthorizedException("Access denied")) + val result = controller.recover(failed) - status(result) must equalTo(FORBIDDEN) + status(result) must equalTo(FORBIDDEN) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala index 8566a524..7740aa99 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareActionSpec.scala @@ -46,212 +46,236 @@ class UserAwareActionSpec extends PlaySpecification with JsonMatchers { "The `UserAwareAction` action" should { "invoke action without identity and authenticator if no authenticator could be found" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - val result = controller.defaultAction(request) + val result = controller.defaultAction(request) - status(result) must equalTo(OK) - contentAsString(result) must contain(messagesApi("without.identity.and.authenticator")) + status(result) must equalTo(OK) + contentAsString(result) must contain(messagesApi("without.identity.and.authenticator")) + } } } "invoke action without identity and authenticator if invalid authenticator was found" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator.copy(isValid = false)))) - when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) - } + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator.copy(isValid = false)))) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } - val result = controller.defaultAction(request) + val result = controller.defaultAction(request) - status(result) must equalTo(OK) - contentAsString(result) must contain(messagesApi("without.identity.and.authenticator")) - verify(env.authenticatorService).discard(any(), any())(any()) + status(result) must equalTo(OK) + contentAsString(result) must contain(messagesApi("without.identity.and.authenticator")) + verify(env.authenticatorService).discard(any(), any())(any()) + } } } "invoke action with valid authenticator if no identity could be found" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) + + val result = controller.defaultAction(request) + + status(result) must equalTo(OK) + contentAsString(result) must contain(messagesApi("without.identity.and.with.authenticator")) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(None)) - - val result = controller.defaultAction(request) - - status(result) must equalTo(OK) - contentAsString(result) must contain(messagesApi("without.identity.and.with.authenticator")) - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).update(any(), any())(any()) } } "invoke action with authenticator and identity" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + + val result = controller.defaultAction(request) + + status(result) must equalTo(OK) + contentAsString(result) must contain(messagesApi("with.identity.and.authenticator")) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - - val result = controller.defaultAction(request) - - status(result) must equalTo(OK) - contentAsString(result) must contain(messagesApi("with.identity.and.authenticator")) - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).update(any(), any())(any()) } } "use next request provider in the chain if first isn't responsible" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(None)) - when(basicAuthRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) - when(env.authenticatorService.init(any())(any())).thenAnswer { p => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) } - when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + override def running() = { + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(None)) + when(basicAuthRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.init(any())(any())).thenAnswer { p => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) } + when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + + val result = controller.defaultAction(request) + + status(result) must equalTo(OK) + contentAsString(result) must contain("with.identity.and.authenticator") + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).init(any())(any()) } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - - val result = controller.defaultAction(request) - - status(result) must equalTo(OK) - contentAsString(result) must contain("with.identity.and.authenticator") - verify(env.authenticatorService).create(any())(any()) - verify(env.authenticatorService).init(any())(any()) } } "update an initialized authenticator if it was touched" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + + val result = controller.defaultAction(request) + + status(result) must equalTo(OK) + contentAsString(result) must contain("with.identity.and.authenticator") + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) } - - val result = controller.defaultAction(request) - - status(result) must equalTo(OK) - contentAsString(result) must contain("with.identity.and.authenticator") - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).update(any(), any())(any()) } } "do not update an initialized authenticator if it was not touched" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Right(authenticator)) - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Right(authenticator)) + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - val result = controller.defaultAction(request) + val result = controller.defaultAction(request) - status(result) must equalTo(OK) - contentAsString(result) must contain(messagesApi("with.identity.and.authenticator")) - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService, never()).update(any(), any())(any()) + status(result) must equalTo(OK) + contentAsString(result) must contain(messagesApi("with.identity.and.authenticator")) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) + } } } "init an uninitialized authenticator" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) - when(env.authenticatorService.init(any())(any())).thenAnswer { p => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) } - when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + override def running() = { + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.init(any())(any())).thenAnswer { p => Future.successful(p.asInstanceOf[FakeAuthenticator#Value]) } + when(env.authenticatorService.embed(any(), any[Result]())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + + val result = controller.defaultAction(request) + + status(result) must equalTo(OK) + contentAsString(result) must contain("with.identity.and.authenticator") + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).init(any())(any()) } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - - val result = controller.defaultAction(request) - - status(result) must equalTo(OK) - contentAsString(result) must contain("with.identity.and.authenticator") - verify(env.authenticatorService).create(any())(any()) - verify(env.authenticatorService).init(any())(any()) } } "renew an initialized authenticator" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + + val result = controller.renewAction(request) + + status(result) must equalTo(OK) + contentAsString(result) must contain(messagesApi("renewed")) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).renew(any(), any())(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - - val result = controller.renewAction(request) - - status(result) must equalTo(OK) - contentAsString(result) must contain(messagesApi("renewed")) - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).renew(any(), any())(any()) - verify(env.authenticatorService, never()).update(any(), any())(any()) } } "renew an uninitialized authenticator" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) - when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + override def running() = { + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.renew(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + + val result = controller.renewAction(request) + + status(result) must equalTo(OK) + contentAsString(result) must contain("renewed") + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).renew(any(), any())(any()) } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - - val result = controller.renewAction(request) - - status(result) must equalTo(OK) - contentAsString(result) must contain("renewed") - verify(env.authenticatorService).create(any())(any()) - verify(env.authenticatorService).renew(any(), any())(any()) } } "discard an initialized authenticator" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + + val result = controller.discardAction(request) + + status(result) must equalTo(OK) + contentAsString(result) must contain(messagesApi("discarded")) + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).discard(any(), any())(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - - val result = controller.discardAction(request) - - status(result) must equalTo(OK) - contentAsString(result) must contain(messagesApi("discarded")) - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).discard(any(), any())(any()) - verify(env.authenticatorService, never()).update(any(), any())(any()) } } "discard an uninitialized authenticator" in new InjectorContext with WithRequestProvider { new WithApplication(app) with Context { - when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) - when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + override def running() = { + when(tokenRequestProvider.authenticate(any())).thenReturn(Future.successful(Some(identity.loginInfo))) + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + when(env.authenticatorService.create(any())(any())).thenReturn(Future.successful(authenticator)) + when(env.authenticatorService.discard(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + + val result = controller.discardAction(request) + + status(result) must equalTo(OK) + verify(env.authenticatorService).create(any())(any()) + verify(env.authenticatorService).discard(any(), any())(any()) } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - - val result = controller.discardAction(request) - - status(result) must equalTo(OK) - verify(env.authenticatorService).create(any())(any()) - verify(env.authenticatorService).discard(any(), any())(any()) } } } @@ -259,31 +283,35 @@ class UserAwareActionSpec extends PlaySpecification with JsonMatchers { "The `UserAwareRequestHandler`" should { "return status 401 if authentication was not successful" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(None)) - val result = controller.defaultHandler(request) + val result = controller.defaultHandler(request) - status(result) must equalTo(UNAUTHORIZED) - verify(env.authenticatorService, never()).touch(any()) - verify(env.authenticatorService, never()).update(any(), any())(any()) + status(result) must equalTo(UNAUTHORIZED) + verify(env.authenticatorService, never()).touch(any()) + verify(env.authenticatorService, never()).update(any(), any())(any()) + } } } "return the user if authentication was successful" in new InjectorContext { new WithApplication(app) with Context { - when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) - when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) - when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => - Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + override def running() = { + when(env.authenticatorService.retrieve(any())).thenReturn(Future.successful(Some(authenticator))) + when(env.authenticatorService.touch(any())).thenReturn(Left(authenticator)) + when(env.authenticatorService.update(any(), any())(any())).thenAnswer { m => + Future.successful(AuthenticatorResult(m.getArgument(1).asInstanceOf[Result])) + } + when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) + + val result = controller.defaultHandler(request) + + status(result) must equalTo(OK) + contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "1") + verify(env.authenticatorService).touch(any()) + verify(env.authenticatorService).update(any(), any())(any()) } - when(env.identityService.retrieve(identity.loginInfo)).thenReturn(Future.successful(Some(identity))) - - val result = controller.defaultHandler(request) - - status(result) must equalTo(OK) - contentAsString(result) must */("providerID" -> "test") and */("providerKey" -> "1") - verify(env.authenticatorService).touch(any()) - verify(env.authenticatorService).update(any(), any())(any()) } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorResultSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorResultSpec.scala index 3bed0824..b437739b 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorResultSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorResultSpec.scala @@ -34,28 +34,34 @@ class AuthenticatorResultSpec extends PlaySpecification { "The `withSession` method" should { "return new a new instance of an authenticator result" in new WithApplication { - val result = Results.Ok - val authenticatorResult = AuthenticatorResult(result) + override def running() = { + val result = Results.Ok + val authenticatorResult = AuthenticatorResult(result) - authenticatorResult.withSession("name" -> "value") must beAnInstanceOf[AuthenticatorResult] + authenticatorResult.withSession("name" -> "value") must beAnInstanceOf[AuthenticatorResult] + } } } "The `withCookies` method" should { "return new a new instance of an authenticator result" in new WithApplication { - val result = Results.Ok - val authenticatorResult = AuthenticatorResult(result) + override def running() = { + val result = Results.Ok + val authenticatorResult = AuthenticatorResult(result) - authenticatorResult.withCookies(Cookie("name", "value")) must beAnInstanceOf[AuthenticatorResult] + authenticatorResult.withCookies(Cookie("name", "value")) must beAnInstanceOf[AuthenticatorResult] + } } } "The `withHeaders` method" should { "return new a new instance of an authenticator result" in new WithApplication { - val result = Results.Ok - val authenticatorResult = AuthenticatorResult(result) + override def running() = { + val result = Results.Ok + val authenticatorResult = AuthenticatorResult(result) - authenticatorResult.withHeaders("name" -> "value") must beAnInstanceOf[AuthenticatorResult] + authenticatorResult.withHeaders("name" -> "value") must beAnInstanceOf[AuthenticatorResult] + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/util/PlayHTTPLayerSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/util/PlayHTTPLayerSpec.scala index bbd54a01..de9cdb76 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/util/PlayHTTPLayerSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/api/util/PlayHTTPLayerSpec.scala @@ -27,13 +27,15 @@ class PlayHTTPLayerSpec extends PlaySpecification { "The `url` method" should { "return a new WS.WSRequest instance" in new WithApplication { - val url = "http://silhouette.mohiva.com" - val client = app.injector.instanceOf[WSClient] - val httpLayer = new PlayHTTPLayer(client) - val requestHolder = httpLayer.url(url) + override def running() = { + val url = "http://silhouette.mohiva.com" + val client = app.injector.instanceOf[WSClient] + val httpLayer = new PlayHTTPLayer(client) + val requestHolder = httpLayer.url(url) - requestHolder should beAnInstanceOf[WSRequest] - requestHolder.url must be equalTo url + requestHolder should beAnInstanceOf[WSRequest] + requestHolder.url must be equalTo url + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala index c2b89853..421b5578 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala @@ -223,22 +223,26 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification { "The `touch` method of the service" should { "update the last used date if idle timeout is defined" in new WithApplication with Context { - when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second)) - when(clock.now).thenReturn(ZonedDateTime.now) - - service.touch(authenticator) must beLeft[BearerTokenAuthenticator].like { - case a => - a.lastUsedDateTime must be equalTo clock.now + override def running() = { + when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second)) + when(clock.now).thenReturn(ZonedDateTime.now) + + service.touch(authenticator) must beLeft[BearerTokenAuthenticator].like { + case a => + a.lastUsedDateTime must be equalTo clock.now + } } } "do not update the last used date if idle timeout is not defined" in new WithApplication with Context { - when(settings.authenticatorIdleTimeout).thenReturn(None) - when(clock.now).thenReturn(ZonedDateTime.now) - - service.touch(authenticator) must beRight[BearerTokenAuthenticator].like { - case a => - a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime + override def running() = { + when(settings.authenticatorIdleTimeout).thenReturn(None) + when(clock.now).thenReturn(ZonedDateTime.now) + + service.touch(authenticator) must beRight[BearerTokenAuthenticator].like { + case a => + a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala index 697dc501..35c01b39 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala @@ -65,48 +65,60 @@ class CookieAuthenticatorSpec extends PlaySpecification { "The `serialize` method of the authenticator" should { "sign the cookie" in new WithApplication with Context { - serialize(authenticator, signer, authenticatorEncoder) + override def running() = { + serialize(authenticator, signer, authenticatorEncoder) - verify(signer).sign(any()) + verify(signer).sign(any()) + } } "encode the cookie" in new WithApplication with Context { - serialize(authenticator, signer, authenticatorEncoder) + override def running() = { + serialize(authenticator, signer, authenticatorEncoder) - verify(authenticatorEncoder).encode(any()) + verify(authenticatorEncoder).encode(any()) + } } } "The `unserialize` method of the authenticator" should { "throw an AuthenticatorException if the given value can't be parsed as Json" in new WithApplication with Context { - val value = "invalid" - val msg = Pattern.quote(InvalidJson.format(ID, value)) + override def running() = { + val value = "invalid" + val msg = Pattern.quote(InvalidJson.format(ID, value)) - unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg) + unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg) + } } "throw an AuthenticatorException if the given value is in the wrong Json format" in new WithApplication with Context { - val value = "{}" - val msg = "^" + Pattern.quote(InvalidJsonFormat.format(ID, "")) + ".*" + override def running() = { + val value = "{}" + val msg = "^" + Pattern.quote(InvalidJsonFormat.format(ID, "")) + ".*" - unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg) + unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg) + } } "throw an AuthenticatorException if the cookie signer declines the authenticator" in new WithApplication with Context { - val value = "value" - val msg = "^" + Pattern.quote(InvalidCookieSignature.format(ID, "")) + ".*" + override def running() = { + val value = "value" + val msg = "^" + Pattern.quote(InvalidCookieSignature.format(ID, "")) + ".*" - when(signer.extract(any())).thenReturn(Failure(new Exception("invalid"))) + when(signer.extract(any())).thenReturn(Failure(new Exception("invalid"))) - unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg) + unserialize(authenticatorEncoder.encode(value), signer, authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg) + } } } "The `serialize/unserialize` method of the authenticator" should { "serialize/unserialize an authenticator" in new WithApplication with Context { - val value = serialize(authenticator, signer, authenticatorEncoder) + override def running() = { + val value = serialize(authenticator, signer, authenticatorEncoder) - unserialize(value, signer, authenticatorEncoder) must beSuccessfulTry.withValue(authenticator) + unserialize(value, signer, authenticatorEncoder) must beSuccessfulTry.withValue(authenticator) + } } } @@ -202,10 +214,12 @@ class CookieAuthenticatorSpec extends PlaySpecification { } "[stateless] return None if no authenticator could be unserialized from cookie" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticatorEncoder.encode("invalid"))) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, authenticatorEncoder.encode("invalid"))) - await(service(None).retrieve) must beNone - verify(repository, never()).find(any()) + await(service(None).retrieve) must beNone + verify(repository, never()).find(any()) + } } "[stateful] return None if authenticator fingerprint doesn't match current fingerprint" in new Context { @@ -220,14 +234,16 @@ class CookieAuthenticatorSpec extends PlaySpecification { } "[stateless] return None if authenticator fingerprint doesn't match current fingerprint" in new WithApplication with Context { - when(fingerprintGenerator.generate(any())).thenReturn("false") - when(settings.useFingerprinting).thenReturn(true) - when(authenticator.fingerprint).thenReturn(Some("test")) + override def running() = { + when(fingerprintGenerator.generate(any())).thenReturn("false") + when(settings.useFingerprinting).thenReturn(true) + when(authenticator.fingerprint).thenReturn(Some("test")) - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder))) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder))) - await(service(None).retrieve) must beNone - verify(repository, never()).find(any()) + await(service(None).retrieve) must beNone + verify(repository, never()).find(any()) + } } "[stateful] return authenticator if authenticator fingerprint matches current fingerprint" in new Context { @@ -242,14 +258,16 @@ class CookieAuthenticatorSpec extends PlaySpecification { } "[stateless] return authenticator if authenticator fingerprint matches current fingerprint" in new WithApplication with Context { - when(fingerprintGenerator.generate(any())).thenReturn("test") - when(settings.useFingerprinting).thenReturn(true) - when(authenticator.fingerprint).thenReturn(Some("test")) + override def running() = { + when(fingerprintGenerator.generate(any())).thenReturn("test") + when(settings.useFingerprinting).thenReturn(true) + when(authenticator.fingerprint).thenReturn(Some("test")) - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder))) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder))) - await(service(None).retrieve) must beSome(authenticator) - verify(repository, never()).find(any()) + await(service(None).retrieve) must beSome(authenticator) + verify(repository, never()).find(any()) + } } "[stateful] return authenticator if fingerprinting is disabled" in new Context { @@ -262,13 +280,15 @@ class CookieAuthenticatorSpec extends PlaySpecification { } "[stateless] return authenticator if fingerprinting is disabled" in new WithApplication with Context { - when(settings.useFingerprinting).thenReturn(false) + override def running() = { + when(settings.useFingerprinting).thenReturn(false) - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder))) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator, signer, authenticatorEncoder))) - when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator))) + when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator))) - await(service(None).retrieve) must beSome(authenticator) + await(service(None).retrieve) must beSome(authenticator) + } } "throws an AuthenticatorRetrievalException exception if an error occurred during retrieval" in new Context { @@ -296,12 +316,14 @@ class CookieAuthenticatorSpec extends PlaySpecification { } "[stateless] return a cookie with a serialized authenticator" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val cookie = await(service(None).init(authenticator)) + val cookie = await(service(None).init(authenticator)) - unserialize(cookie.value, signer, authenticatorEncoder) must be equalTo unserialize(statelessCookie.value, signer, authenticatorEncoder) - verify(repository, never()).add(any()) + unserialize(cookie.value, signer, authenticatorEncoder) must be equalTo unserialize(statelessCookie.value, signer, authenticatorEncoder) + verify(repository, never()).add(any()) + } } "throws an AuthenticatorInitializationException exception if an error occurred during initialization" in new Context { @@ -359,22 +381,26 @@ class CookieAuthenticatorSpec extends PlaySpecification { "The `touch` method of the service" should { "update the last used date if idle timeout is defined" in new WithApplication with Context { - when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second)) - when(clock.now).thenReturn(ZonedDateTime.now) - - service(Some(repository)).touch(authenticator) must beLeft[CookieAuthenticator].like { - case a => - a.lastUsedDateTime must be equalTo clock.now + override def running() = { + when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second)) + when(clock.now).thenReturn(ZonedDateTime.now) + + service(Some(repository)).touch(authenticator) must beLeft[CookieAuthenticator].like { + case a => + a.lastUsedDateTime must be equalTo clock.now + } } } "do not update the last used date if idle timeout is not defined" in new WithApplication with Context { - when(settings.authenticatorIdleTimeout).thenReturn(None) - when(clock.now).thenReturn(ZonedDateTime.now) - - service(Some(repository)).touch(authenticator) must beRight[CookieAuthenticator].like { - case a => - a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime + override def running() = { + when(settings.authenticatorIdleTimeout).thenReturn(None) + when(clock.now).thenReturn(ZonedDateTime.now) + + service(Some(repository)).touch(authenticator) must beRight[CookieAuthenticator].like { + case a => + a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime + } } } } @@ -400,12 +426,14 @@ class CookieAuthenticatorSpec extends PlaySpecification { } "[stateless] update the cookie for the updated authenticator" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val result = service(None).update(authenticator, Results.Ok) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val result = service(None).update(authenticator, Results.Ok) - status(result) must be equalTo OK - cookies(result).get(settings.cookieName) should beSome[Cookie].which(statelessResponseCookieMatcher(authenticator)) - verify(repository, never()).update(authenticator) + status(result) must be equalTo OK + cookies(result).get(settings.cookieName) should beSome[Cookie].which(statelessResponseCookieMatcher(authenticator)) + verify(repository, never()).update(authenticator) + } } "throws an AuthenticatorUpdateException exception if an error occurred during update" in new Context { @@ -453,19 +481,21 @@ class CookieAuthenticatorSpec extends PlaySpecification { } "[stateless] renew the authenticator and return the response with the updated cookie value" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val now = ZonedDateTime.now - val id = "new-test-id" + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val now = ZonedDateTime.now + val id = "new-test-id" - when(settings.useFingerprinting).thenReturn(false) - when(idGenerator.generate).thenReturn(Future.successful(id)) - when(clock.now).thenReturn(now) + when(settings.useFingerprinting).thenReturn(false) + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(now) - val result = service(None).renew(authenticator, Results.Ok) + val result = service(None).renew(authenticator, Results.Ok) - cookies(result).get(settings.cookieName) should beSome[Cookie].which(statelessResponseCookieMatcher( - authenticator.copy(id = id, lastUsedDateTime = now, expirationDateTime = now + settings.authenticatorExpiry))) - verify(repository, never()).add(any()) + cookies(result).get(settings.cookieName) should beSome[Cookie].which(statelessResponseCookieMatcher( + authenticator.copy(id = id, lastUsedDateTime = now, expirationDateTime = now + settings.authenticatorExpiry))) + verify(repository, never()).add(any()) + } } "throws an AuthenticatorRenewalException exception if an error occurred during renewal" in new Context { @@ -505,18 +535,20 @@ class CookieAuthenticatorSpec extends PlaySpecification { } "[stateless] discard the cookie from response" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val result = service(None).discard(authenticator, Results.Ok.withCookies(statelessCookie)) - - cookies(result).get(settings.cookieName) should beSome[Cookie].which { c => - c.name must be equalTo settings.cookieName - c.value must be equalTo "" - c.maxAge must beSome(Cookie.DiscardedMaxAge) - c.path must be equalTo settings.cookiePath - c.domain must be equalTo settings.cookieDomain - c.secure must be equalTo settings.secureCookie + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val result = service(None).discard(authenticator, Results.Ok.withCookies(statelessCookie)) + + cookies(result).get(settings.cookieName) should beSome[Cookie].which { c => + c.name must be equalTo settings.cookieName + c.value must be equalTo "" + c.maxAge must beSome(Cookie.DiscardedMaxAge) + c.path must be equalTo settings.cookiePath + c.domain must be equalTo settings.cookieDomain + c.secure must be equalTo settings.secureCookie + } + verify(repository, never()).remove(authenticator.id) } - verify(repository, never()).remove(authenticator.id) } "throws an AuthenticatorDiscardingException exception if an error occurred during discarding" in new Context { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/DummyAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/DummyAuthenticatorSpec.scala index 574fd9e5..bfe3d49c 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/DummyAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/DummyAuthenticatorSpec.scala @@ -77,9 +77,11 @@ class DummyAuthenticatorSpec extends PlaySpecification { "The `touch` method of the service" should { "not update the authenticator" in new WithApplication with Context { - service.touch(authenticator) must beRight[DummyAuthenticator].like { - case a => - a.loginInfo must be equalTo loginInfo + override def running() = { + service.touch(authenticator) must beRight[DummyAuthenticator].like { + case a => + a.loginInfo must be equalTo loginInfo + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala index 649f447c..d3bbc18a 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala @@ -65,128 +65,153 @@ class JWTAuthenticatorSpec extends PlaySpecification with JsonMatchers { "The `serialize` method of the authenticator" should { "return a JWT with an expiration time" in new WithApplication with Context { - val jwt = serialize(authenticator, authenticatorEncoder, settings) - val json = Base64.decode(jwt.split('.').apply(1)) + override def running() = { + val jwt = serialize(authenticator, authenticatorEncoder, settings) + val json = Base64.decode(jwt.split('.').apply(1)) - json must /("exp" -> authenticator.expirationDateTime.toEpochSecond.toInt) + json must /("exp" -> authenticator.expirationDateTime.toEpochSecond.toInt) + } } "return a JWT with an encoded subject" in new WithApplication with Context { - val jwt = serialize(authenticator, authenticatorEncoder, settings) - val json = Json.parse(Base64.decode(jwt.split('.').apply(1))) - val sub = Json.parse(authenticatorEncoder.decode((json \ "sub").as[String])).as[LoginInfo] + override def running() = { + val jwt = serialize(authenticator, authenticatorEncoder, settings) + val json = Json.parse(Base64.decode(jwt.split('.').apply(1))) + val sub = Json.parse(authenticatorEncoder.decode((json \ "sub").as[String])).as[LoginInfo] - sub must be equalTo authenticator.loginInfo + sub must be equalTo authenticator.loginInfo + } } "return a JWT with an issuer" in new WithApplication with Context { - val jwt = serialize(authenticator, authenticatorEncoder, settings) - val json = Base64.decode(jwt.split('.').apply(1)) + override def running() = { + val jwt = serialize(authenticator, authenticatorEncoder, settings) + val json = Base64.decode(jwt.split('.').apply(1)) - json must /("iss" -> settings.issuerClaim) + json must /("iss" -> settings.issuerClaim) + } } "return a JWT with an issued-at time" in new WithApplication with Context { - val jwt = serialize(authenticator, authenticatorEncoder, settings) - val json = Base64.decode(jwt.split('.').apply(1)) + override def running() = { + val jwt = serialize(authenticator, authenticatorEncoder, settings) + val json = Base64.decode(jwt.split('.').apply(1)) - json must /("iat" -> authenticator.lastUsedDateTime.toEpochSecond.toInt) + json must /("iat" -> authenticator.lastUsedDateTime.toEpochSecond.toInt) + } } "throw an AuthenticatorException if a reserved claim will be overridden" in new WithApplication with Context { - val claims = Json.obj( - "jti" -> "reserved") + override def running() = { + val claims = Json.obj( + "jti" -> "reserved") - serialize(authenticator.copy(customClaims = Some(claims)), authenticatorEncoder, settings) must throwA[AuthenticatorException].like { - case e => e.getMessage must startWith(OverrideReservedClaim.format(ID, "jti", "")) + serialize(authenticator.copy(customClaims = Some(claims)), authenticatorEncoder, settings) must throwA[AuthenticatorException].like { + case e => e.getMessage must startWith(OverrideReservedClaim.format(ID, "jti", "")) + } } } "throw an AuthenticatorException if an unexpected value was found in the arbitrary claims" in new WithApplication with Context { - val claims = Json.obj( - "null" -> JsNull) + override def running() = { + val claims = Json.obj( + "null" -> JsNull) - serialize(authenticator.copy(customClaims = Some(claims)), authenticatorEncoder, settings) must throwA[AuthenticatorException].like { - case e => e.getMessage must startWith(UnexpectedJsonValue.format(ID, "")) + serialize(authenticator.copy(customClaims = Some(claims)), authenticatorEncoder, settings) must throwA[AuthenticatorException].like { + case e => e.getMessage must startWith(UnexpectedJsonValue.format(ID, "")) + } } } "return a JWT with arbitrary claims" in new WithApplication with Context { - val jwt = serialize(authenticator.copy(customClaims = Some(customClaims)), authenticatorEncoder, settings) - val json = Base64.decode(jwt.split('.').apply(1)) - - json must /("boolean" -> true) - json must /("string" -> "string") - json must /("number" -> 1234567890) - json must /("array") /# 0 / 1 - json must /("array") /# 1 / 2 - json must /("object") / "array" /# 0 / "string1" - json must /("object") / "array" /# 1 / "string2" - json must /("object") / "object" / "array" /# 0 / "string" - json must /("object") / "object" / "array" /# 1 / false - json must /("object") / "object" / "array" /# 2 / ("number" -> 1) + override def running() = { + val jwt = serialize(authenticator.copy(customClaims = Some(customClaims)), authenticatorEncoder, settings) + val json = Base64.decode(jwt.split('.').apply(1)) + + json must /("boolean" -> true) + json must /("string" -> "string") + json must /("number" -> 1234567890) + json must /("array") /# 0 / 1 + json must /("array") /# 1 / 2 + json must /("object") / "array" /# 0 / "string1" + json must /("object") / "array" /# 1 / "string2" + json must /("object") / "object" / "array" /# 0 / "string" + json must /("object") / "object" / "array" /# 1 / false + json must /("object") / "object" / "array" /# 2 / ("number" -> 1) + } } } "The `unserialize` method of the authenticator" should { "throw an AuthenticatorException if the given token can't be parsed" in new WithApplication with Context { - val jwt = "invalid" - val msg = Pattern.quote(InvalidJWTToken.format(ID, jwt)) + override def running() = { + val jwt = "invalid" + val msg = Pattern.quote(InvalidJWTToken.format(ID, jwt)) - unserialize(jwt, authenticatorEncoder, settings) must beFailedTry.withThrowable[AuthenticatorException](msg) + unserialize(jwt, authenticatorEncoder, settings) must beFailedTry.withThrowable[AuthenticatorException](msg) + } } "throw an AuthenticatorException if the given token couldn't be verified" in new WithApplication with Context { - val jwt = serialize(authenticator, authenticatorEncoder, settings) + "-wrong-sig" - val msg = Pattern.quote(InvalidJWTToken.format(ID, jwt)) + override def running() = { + val jwt = serialize(authenticator, authenticatorEncoder, settings) + "-wrong-sig" + val msg = Pattern.quote(InvalidJWTToken.format(ID, jwt)) - unserialize(jwt, authenticatorEncoder, settings) must beFailedTry.withThrowable[AuthenticatorException](msg) + unserialize(jwt, authenticatorEncoder, settings) must beFailedTry.withThrowable[AuthenticatorException](msg) + } } "unserialize a JWT" in new WithApplication with Context { - val jwt = serialize(authenticator, authenticatorEncoder, settings) + override def running() = { + val jwt = serialize(authenticator, authenticatorEncoder, settings) - unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticator.copy( - expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), - lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) + unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticator.copy( + expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), + lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) + } } "unserialize a JWT with a custom clock" in new WithApplication with Context { + override def running() = { + val lastUsedDateTime: ZonedDateTime = ZonedDateTime + .of(2015, 2, 25, 19, 0, 0, 0, ZoneId.systemDefault()) + .`with`(ChronoField.MILLI_OF_SECOND, 0) - val lastUsedDateTime: ZonedDateTime = ZonedDateTime - .of(2015, 2, 25, 19, 0, 0, 0, ZoneId.systemDefault()) - .`with`(ChronoField.MILLI_OF_SECOND, 0) + val authenticatorCustomClock: JWTAuthenticator = authenticator + .copy( + expirationDateTime = lastUsedDateTime + settings.authenticatorExpiry, + lastUsedDateTime = lastUsedDateTime) - val authenticatorCustomClock: JWTAuthenticator = authenticator - .copy( - expirationDateTime = lastUsedDateTime + settings.authenticatorExpiry, - lastUsedDateTime = lastUsedDateTime) + val jwt: String = serialize(authenticatorCustomClock, authenticatorEncoder, settings) - val jwt: String = serialize(authenticatorCustomClock, authenticatorEncoder, settings) + when(clock.now).thenReturn(lastUsedDateTime) + implicit val customClock: Option[Clock] = Some(clock) - when(clock.now).thenReturn(lastUsedDateTime) - implicit val customClock: Option[Clock] = Some(clock) - - unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticatorCustomClock.copy( - expirationDateTime = authenticatorCustomClock.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), - lastUsedDateTime = authenticatorCustomClock.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) + unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticatorCustomClock.copy( + expirationDateTime = authenticatorCustomClock.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), + lastUsedDateTime = authenticatorCustomClock.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) + } } "unserialize a JWT with arbitrary claims" in new WithApplication with Context { - val jwt = serialize(authenticator.copy(customClaims = Some(customClaims)), authenticatorEncoder, settings) + override def running() = { + val jwt = serialize(authenticator.copy(customClaims = Some(customClaims)), authenticatorEncoder, settings) - unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.like { - case a => - a.customClaims must beSome(customClaims) + unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.like { + case a => + a.customClaims must beSome(customClaims) + } } } } "The `serialize/unserialize` method of the authenticator" should { "serialize/unserialize an authenticator" in new WithApplication with Context { - val jwt = serialize(authenticator, authenticatorEncoder, settings) + override def running() = { + val jwt = serialize(authenticator, authenticatorEncoder, settings) - unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticator) + unserialize(jwt, authenticatorEncoder, settings) must beSuccessfulTry.withValue(authenticator) + } } } @@ -261,84 +286,98 @@ class JWTAuthenticatorSpec extends PlaySpecification with JsonMatchers { } "return authenticator if DAO is enabled and an authenticator is stored for the token located in the the header" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings)) - when(clock.now).thenReturn(ZonedDateTime.now) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings)) + when(clock.now).thenReturn(ZonedDateTime.now) - when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator.copy( - expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), - lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))))) + when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator.copy( + expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), + lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))))) - await(service(Some(repository)).retrieve) must beSome(authenticator.copy( - expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), - lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) + await(service(Some(repository)).retrieve) must beSome(authenticator.copy( + expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), + lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) + } } "return authenticator if DAO is enabled and an authenticator is stored for the token located in the the query string" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest("GET", s"?${settings.fieldName}=${serialize(authenticator, authenticatorEncoder, settings)}") - when(clock.now).thenReturn(ZonedDateTime.now) - - when(settings.requestParts).thenReturn(Some(Seq(RequestPart.QueryString))) - when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator.copy( - expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), - lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))))) - - await(service(Some(repository)).retrieve) must beSome(authenticator.copy( - expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), - lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest("GET", s"?${settings.fieldName}=${serialize(authenticator, authenticatorEncoder, settings)}") + when(clock.now).thenReturn(ZonedDateTime.now) + + when(settings.requestParts).thenReturn(Some(Seq(RequestPart.QueryString))) + when(repository.find(authenticator.id)).thenReturn(Future.successful(Some(authenticator.copy( + expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), + lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))))) + + await(service(Some(repository)).retrieve) must beSome(authenticator.copy( + expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), + lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) + } } "return authenticator if DAO is disabled and authenticator was found in the header" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings)) - when(clock.now).thenReturn(ZonedDateTime.now) - - await(service(None).retrieve) must beSome(authenticator.copy( - expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), - lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) - verify(repository, never()).find(any()) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings)) + when(clock.now).thenReturn(ZonedDateTime.now) + + await(service(None).retrieve) must beSome(authenticator.copy( + expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), + lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) + verify(repository, never()).find(any()) + } } "return authenticator if DAO is disabled and authenticator was found in the query string" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest("GET", s"?${settings.fieldName}=${serialize(authenticator, authenticatorEncoder, settings)}") - when(clock.now).thenReturn(ZonedDateTime.now) - - when(settings.requestParts).thenReturn(Some(Seq(RequestPart.QueryString))) - await(service(None).retrieve) must beSome(authenticator.copy( - expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), - lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) - verify(repository, never()).find(any()) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest("GET", s"?${settings.fieldName}=${serialize(authenticator, authenticatorEncoder, settings)}") + when(clock.now).thenReturn(ZonedDateTime.now) + + when(settings.requestParts).thenReturn(Some(Seq(RequestPart.QueryString))) + await(service(None).retrieve) must beSome(authenticator.copy( + expirationDateTime = authenticator.expirationDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0), + lastUsedDateTime = authenticator.lastUsedDateTime.`with`(ChronoField.MILLI_OF_SECOND, 0))) + verify(repository, never()).find(any()) + } } "throws an AuthenticatorRetrievalException exception if an error occurred during retrieval" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings)) - when(clock.now).thenReturn(ZonedDateTime.now) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withHeaders(settings.fieldName -> serialize(authenticator, authenticatorEncoder, settings)) + when(clock.now).thenReturn(ZonedDateTime.now) - when(repository.find(authenticator.id)).thenReturn(Future.failed(new RuntimeException("Cannot find authenticator"))) + when(repository.find(authenticator.id)).thenReturn(Future.failed(new RuntimeException("Cannot find authenticator"))) - await(service(Some(repository)).retrieve) must throwA[AuthenticatorRetrievalException].like { - case e => - e.getMessage must startWith(RetrieveError.format(ID, "")) + await(service(Some(repository)).retrieve) must throwA[AuthenticatorRetrievalException].like { + case e => + e.getMessage must startWith(RetrieveError.format(ID, "")) + } } } } "The `init` method of the service" should { "return the token if DAO is enabled and authenticator could be saved in backing store" in new WithApplication with Context { - when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + override def running() = { + when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val token = await(service(Some(repository)).init(authenticator)) + val token = await(service(Some(repository)).init(authenticator)) - unserialize(token, authenticatorEncoder, settings).get must be equalTo authenticator - verify(repository).add(any()) + unserialize(token, authenticatorEncoder, settings).get must be equalTo authenticator + verify(repository).add(any()) + } } "return the token if DAO is disabled" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val token = await(service(None).init(authenticator)) + val token = await(service(None).init(authenticator)) - unserialize(token, authenticatorEncoder, settings).get must be equalTo authenticator - verify(repository, never()).add(any()) + unserialize(token, authenticatorEncoder, settings).get must be equalTo authenticator + verify(repository, never()).add(any()) + } } "throws an AuthenticatorInitializationException exception if an error occurred during initialization" in new Context { @@ -356,171 +395,199 @@ class JWTAuthenticatorSpec extends PlaySpecification with JsonMatchers { "The result `embed` method of the service" should { "return the response with a header" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val token = serialize(authenticator, authenticatorEncoder, settings) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val token = serialize(authenticator, authenticatorEncoder, settings) - val result = service(Some(repository)).embed(token, Results.Ok) + val result = service(Some(repository)).embed(token, Results.Ok) - header(settings.fieldName, result) should beSome(token) + header(settings.fieldName, result) should beSome(token) + } } } "The request `embed` method of the service" should { "return the request with a header " in new WithApplication with Context { - val token = serialize(authenticator, authenticatorEncoder, settings) - val request = service(Some(repository)).embed(token, FakeRequest()) + override def running() = { + val token = serialize(authenticator, authenticatorEncoder, settings) + val request = service(Some(repository)).embed(token, FakeRequest()) - unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator + unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator + } } "override an existing token" in new WithApplication with Context { - val token = serialize(authenticator, authenticatorEncoder, settings) - val request = service(Some(repository)).embed(token, FakeRequest().withHeaders(settings.fieldName -> "test")) + override def running() = { + val token = serialize(authenticator, authenticatorEncoder, settings) + val request = service(Some(repository)).embed(token, FakeRequest().withHeaders(settings.fieldName -> "test")) - unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator + unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator + } } "keep non authenticator related headers" in new WithApplication with Context { - val token = serialize(authenticator, authenticatorEncoder, settings) - val request = service(Some(repository)).embed(token, FakeRequest().withHeaders("test" -> "test")) + override def running() = { + val token = serialize(authenticator, authenticatorEncoder, settings) + val request = service(Some(repository)).embed(token, FakeRequest().withHeaders("test" -> "test")) - unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator - request.headers.get("test") should beSome("test") + unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator + request.headers.get("test") should beSome("test") + } } "keep other request parts" in new WithApplication with Context { - val token = serialize(authenticator, authenticatorEncoder, settings) - val request = service(Some(repository)).embed(token, FakeRequest().withSession("test" -> "test")) + override def running() = { + val token = serialize(authenticator, authenticatorEncoder, settings) + val request = service(Some(repository)).embed(token, FakeRequest().withSession("test" -> "test")) - unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator - request.session.get("test") should beSome("test") + unserialize(request.headers.get(settings.fieldName).get, authenticatorEncoder, settings).get must be equalTo authenticator + request.session.get("test") should beSome("test") + } } } "The `touch` method of the service" should { "update the last used date if idle timeout is defined" in new WithApplication with Context { - when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second)) - when(clock.now).thenReturn(ZonedDateTime.now) - - service(None).touch(authenticator) must beLeft[JWTAuthenticator].like { - case a => - a.lastUsedDateTime must be equalTo clock.now + override def running() = { + when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second)) + when(clock.now).thenReturn(ZonedDateTime.now) + + service(None).touch(authenticator) must beLeft[JWTAuthenticator].like { + case a => + a.lastUsedDateTime must be equalTo clock.now + } } } "do not update the last used date if idle timeout is not defined" in new WithApplication with Context { - when(settings.authenticatorIdleTimeout).thenReturn(None) - when(clock.now).thenReturn(ZonedDateTime.now) - - service(None).touch(authenticator) must beRight[JWTAuthenticator].like { - case a => - a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime + override def running() = { + when(settings.authenticatorIdleTimeout).thenReturn(None) + when(clock.now).thenReturn(ZonedDateTime.now) + + service(None).touch(authenticator) must beRight[JWTAuthenticator].like { + case a => + a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime + } } } } "The `update` method of the service" should { "update the authenticator in backing store" in new WithApplication with Context { - when(repository.update(any())).thenAnswer { _ => Future.successful(authenticator) } + override def running() = { + when(repository.update(any())).thenAnswer { _ => Future.successful(authenticator) } - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - await(service(Some(repository)).update(authenticator, Results.Ok)) + await(service(Some(repository)).update(authenticator, Results.Ok)) - verify(repository).update(authenticator) + verify(repository).update(authenticator) + } } "return the result if the authenticator could be stored in backing store" in new WithApplication with Context { - when(repository.update(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } + override def running() = { + when(repository.update(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val result = service(Some(repository)).update(authenticator, Results.Ok) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val result = service(Some(repository)).update(authenticator, Results.Ok) - status(result) must be equalTo OK - unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator - verify(repository).update(authenticator) + status(result) must be equalTo OK + unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator + verify(repository).update(authenticator) + } } "return the result if backing store is disabled" in new WithApplication with Context { - when(repository.update(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } + override def running() = { + when(repository.update(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val result = service(None).update(authenticator, Results.Ok) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val result = service(None).update(authenticator, Results.Ok) - status(result) must be equalTo OK - unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator - verify(repository, never()).update(any()) + status(result) must be equalTo OK + unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator + verify(repository, never()).update(any()) + } } "throws an AuthenticatorUpdateException exception if an error occurred during update" in new WithApplication with Context { - when(repository.update(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator"))) + override def running() = { + when(repository.update(any())).thenReturn(Future.failed(new Exception("Cannot store authenticator"))) - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - await(service(Some(repository)).update(authenticator, Results.Ok)) must throwA[AuthenticatorUpdateException].like { - case e => - e.getMessage must startWith(UpdateError.format(ID, "")) + await(service(Some(repository)).update(authenticator, Results.Ok)) must throwA[AuthenticatorUpdateException].like { + case e => + e.getMessage must startWith(UpdateError.format(ID, "")) + } } } } "The `renew` method of the service" should { "renew the authenticator and return the response with a new JWT if DAO is enabled" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val id = "new-test-id" + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val id = "new-test-id" - when(repository.remove(any())).thenAnswer { _ => Future.successful(()) } - when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } - when(idGenerator.generate).thenReturn(Future.successful(id)) - when(clock.now).thenReturn(ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)) + when(repository.remove(any())).thenAnswer { _ => Future.successful(()) } + when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)) - val result = service(Some(repository)).renew(authenticator, Results.Ok) + val result = service(Some(repository)).renew(authenticator, Results.Ok) - unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator.copy( - id = id, - expirationDateTime = clock.now + settings.authenticatorExpiry, - lastUsedDateTime = clock.now) + unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator.copy( + id = id, + expirationDateTime = clock.now + settings.authenticatorExpiry, + lastUsedDateTime = clock.now) - verify(repository).add(any()) - verify(repository).remove(authenticator.id) + verify(repository).add(any()) + verify(repository).remove(authenticator.id) + } } "renew an authenticator with custom claims" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val id = "new-test-id" + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val id = "new-test-id" - when(repository.remove(any())).thenReturn(Future.successful(())) - when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } - when(idGenerator.generate).thenReturn(Future.successful(id)) - when(clock.now).thenReturn(ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)) + when(repository.remove(any())).thenReturn(Future.successful(())) + when(repository.add(any())).thenAnswer { p => Future.successful(p.getArgument(0).asInstanceOf[JWTAuthenticator]) } + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)) - val result = service(Some(repository)).renew(authenticator.copy(customClaims = Some(customClaims)), Results.Ok) + val result = service(Some(repository)).renew(authenticator.copy(customClaims = Some(customClaims)), Results.Ok) - unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator.copy( - id = id, - expirationDateTime = clock.now + settings.authenticatorExpiry, - lastUsedDateTime = clock.now, - customClaims = Some(customClaims)) + unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator.copy( + id = id, + expirationDateTime = clock.now + settings.authenticatorExpiry, + lastUsedDateTime = clock.now, + customClaims = Some(customClaims)) - verify(repository).add(any()) - verify(repository).remove(authenticator.id) + verify(repository).add(any()) + verify(repository).remove(authenticator.id) + } } "renew the authenticator and return the response with a new JWT if DAO is disabled" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val id = "new-test-id" + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val id = "new-test-id" - when(idGenerator.generate).thenReturn(Future.successful(id)) - when(clock.now).thenReturn(ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)) + when(idGenerator.generate).thenReturn(Future.successful(id)) + when(clock.now).thenReturn(ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0)) - val result = service(None).renew(authenticator, Results.Ok) + val result = service(None).renew(authenticator, Results.Ok) - unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator.copy( - id = id, - expirationDateTime = clock.now + settings.authenticatorExpiry, - lastUsedDateTime = clock.now) - verify(repository, never()).remove(any()) - verify(repository, never()).add(any()) + unserialize(header(settings.fieldName, result).get, authenticatorEncoder, settings).get must be equalTo authenticator.copy( + id = id, + expirationDateTime = clock.now + settings.authenticatorExpiry, + lastUsedDateTime = clock.now) + verify(repository, never()).remove(any()) + verify(repository, never()).add(any()) + } } "throws an AuthenticatorRenewalException exception if an error occurred during renewal" in new Context { diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala index 50d6a8b6..c6b807cf 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/SessionAuthenticatorSpec.scala @@ -63,25 +63,31 @@ class SessionAuthenticatorSpec extends PlaySpecification { "The `unserialize` method of the authenticator" should { "throw an AuthenticatorException if the given value can't be parsed as Json" in new WithApplication with Context { - val value = "invalid" - val msg = Pattern.quote(JsonParseError.format(ID, value)) + override def running() = { + val value = "invalid" + val msg = Pattern.quote(JsonParseError.format(ID, value)) - unserialize(authenticatorEncoder.encode(value), authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg) + unserialize(authenticatorEncoder.encode(value), authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg) + } } "throw an AuthenticatorException if the given value is in the wrong Json format" in new WithApplication with Context { - val value = "{}" - val msg = "^" + Pattern.quote(InvalidJsonFormat.format(ID, "")) + ".*" + override def running() = { + val value = "{}" + val msg = "^" + Pattern.quote(InvalidJsonFormat.format(ID, "")) + ".*" - unserialize(authenticatorEncoder.encode(value), authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg) + unserialize(authenticatorEncoder.encode(value), authenticatorEncoder) must beFailedTry.withThrowable[AuthenticatorException](msg) + } } } "The `serialize/unserialize` method of the authenticator" should { "serialize/unserialize an authenticator" in new WithApplication with Context { - val value = serialize(authenticator, authenticatorEncoder) + override def running() = { + val value = serialize(authenticator, authenticatorEncoder) - unserialize(value, authenticatorEncoder) must beSuccessfulTry.withValue(authenticator) + unserialize(value, authenticatorEncoder) must beSuccessfulTry.withValue(authenticator) + } } } @@ -154,210 +160,254 @@ class SessionAuthenticatorSpec extends PlaySpecification { } "return None if session contains invalid json" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode("{")) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode("{")) - when(settings.useFingerprinting).thenReturn(false) + when(settings.useFingerprinting).thenReturn(false) - await(service.retrieve) must beNone + await(service.retrieve) must beNone + } } "return None if session contains valid json but invalid authenticator" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode("{ \"test\": \"test\" }")) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode("{ \"test\": \"test\" }")) - when(settings.useFingerprinting).thenReturn(false) + when(settings.useFingerprinting).thenReturn(false) - await(service.retrieve) must beNone + await(service.retrieve) must beNone + } } "return None if authenticator fingerprint doesn't match current fingerprint" in new WithApplication with Context { - when(fingerprintGenerator.generate(any)).thenReturn("false") - when(settings.useFingerprinting).thenReturn(true) - when(authenticator.fingerprint).thenReturn(Some("test")) + override def running() = { + when(fingerprintGenerator.generate(any)).thenReturn("false") + when(settings.useFingerprinting).thenReturn(true) + when(authenticator.fingerprint).thenReturn(Some("test")) - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) - await(service.retrieve) must beNone + await(service.retrieve) must beNone + } } "return authenticator if authenticator fingerprint matches current fingerprint" in new WithApplication with Context { - when(fingerprintGenerator.generate(any)).thenReturn("test") - when(settings.useFingerprinting).thenReturn(true) - when(authenticator.fingerprint).thenReturn(Some("test")) + override def running() = { + when(fingerprintGenerator.generate(any)).thenReturn("test") + when(settings.useFingerprinting).thenReturn(true) + when(authenticator.fingerprint).thenReturn(Some("test")) - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) - await(service.retrieve) must beSome(authenticator) + await(service.retrieve) must beSome(authenticator) + } } "return authenticator if fingerprinting is disabled" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) - when(settings.useFingerprinting).thenReturn(false) + when(settings.useFingerprinting).thenReturn(false) - await(service.retrieve) must beSome(authenticator) + await(service.retrieve) must beSome(authenticator) + } } "decode an authenticator" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - .withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + .withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) - when(settings.useFingerprinting).thenReturn(false) + when(settings.useFingerprinting).thenReturn(false) - await(service.retrieve) must beSome(authenticator) + await(service.retrieve) must beSome(authenticator) + } } "throws an AuthenticatorRetrievalException exception if an error occurred during retrieval" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> authenticatorEncoder.encode(Json.toJson(authenticator).toString())) - when(fingerprintGenerator.generate(any)).thenThrow(new RuntimeException("Could not generate fingerprint")) - when(settings.useFingerprinting).thenReturn(true) + when(fingerprintGenerator.generate(any)).thenThrow(new RuntimeException("Could not generate fingerprint")) + when(settings.useFingerprinting).thenReturn(true) - await(service.retrieve) must throwA[AuthenticatorRetrievalException].like { - case e => - e.getMessage must startWith(RetrieveError.format(ID, "")) + await(service.retrieve) must throwA[AuthenticatorRetrievalException].like { + case e => + e.getMessage must startWith(RetrieveError.format(ID, "")) + } } } } "The `init` method of the service" should { "return a session with an encoded authenticator" in new WithApplication with AppContext { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) - val session = await(service.init(authenticator)) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) + val session = await(service.init(authenticator)) - session must be equalTo sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)) + session must be equalTo sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)) + } } "override existing authenticator from request" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing") - val session = await(service.init(authenticator)) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing") + val session = await(service.init(authenticator)) - unserialize(session.get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator + unserialize(session.get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator + } } "keep non authenticator related session data" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("test" -> "test") - val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) - val session = await(service.init(authenticator)) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("test" -> "test") + val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) + val session = await(service.init(authenticator)) - session.get(settings.sessionKey) should beSome(data) - session.get("test") should beSome("test") + session.get(settings.sessionKey) should beSome(data) + session.get("test") should beSome("test") + } } } "The result `embed` method of the service" should { "return the response with the session" in new WithApplication with AppContext { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) - val result = service.embed(sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)), Results.Ok) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) + val result = service.embed(sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)), Results.Ok) - session(result).get(settings.sessionKey) should beSome(data) + session(result).get(settings.sessionKey) should beSome(data) + } } "override existing authenticator from request" in new WithApplication with AppContext { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing") - val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) - val result = service.embed(sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)), Results.Ok) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing") + val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) + val result = service.embed(sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)), Results.Ok) - session(result).get(settings.sessionKey) should beSome(data) + session(result).get(settings.sessionKey) should beSome(data) + } } "keep non authenticator related session data" in new WithApplication with AppContext { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep") - val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) - val result = service.embed(sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)), Results.Ok.addingToSession( - "result-other" -> "keep")) - - session(result).get(settings.sessionKey) should beSome(data) - session(result).get("request-other") should beSome("keep") - session(result).get("result-other") should beSome("keep") + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep") + val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) + val result = service.embed(sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)), Results.Ok.addingToSession( + "result-other" -> "keep")) + + session(result).get(settings.sessionKey) should beSome(data) + session(result).get("request-other") should beSome("keep") + session(result).get("result-other") should beSome("keep") + } } } "The request `embed` method of the service" should { "return the request with the session" in new WithApplication with AppContext { - val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) - val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)) - val request = service.embed(session, FakeRequest()) + override def running() = { + val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) + val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)) + val request = service.embed(session, FakeRequest()) - request.session.get(settings.sessionKey) should beSome(data) + request.session.get(settings.sessionKey) should beSome(data) + } } "override an existing session" in new WithApplication with AppContext { - val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) - val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)) - val request = service.embed(session, FakeRequest().withSession(settings.sessionKey -> "test")) + override def running() = { + val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) + val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> data)) + val request = service.embed(session, FakeRequest().withSession(settings.sessionKey -> "test")) - request.session.get(settings.sessionKey) should beSome(data) + request.session.get(settings.sessionKey) should beSome(data) + } } "should not remove an existing session key" in new WithApplication with AppContext { - val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> "test")) - val request = service.embed(session, FakeRequest().withSession("existing" -> "test")) + override def running() = { + val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> "test")) + val request = service.embed(session, FakeRequest().withSession("existing" -> "test")) - request.session.get("existing") should beSome("test") - request.session.get(settings.sessionKey) should beSome("test") + request.session.get("existing") should beSome("test") + request.session.get(settings.sessionKey) should beSome("test") + } } "keep other request parts" in new WithApplication with AppContext { - val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> "test")) - val request = service.embed(session, FakeRequest().withCookies(Cookie("test", "test"))) - - request.session.get(settings.sessionKey) should beSome("test") - request.cookies.get("test") should beSome[Cookie].which { c => - c.name must be equalTo "test" - c.value must be equalTo "test" + override def running() = { + val session = sessionCookieBaker.deserialize(Map(settings.sessionKey -> "test")) + val request = service.embed(session, FakeRequest().withCookies(Cookie("test", "test"))) + + request.session.get(settings.sessionKey) should beSome("test") + request.cookies.get("test") should beSome[Cookie].which { c => + c.name must be equalTo "test" + c.value must be equalTo "test" + } } } } "The `touch` method of the service" should { "update the last used date if idle timeout is defined" in new WithApplication with Context { - when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second)) - when(clock.now).thenReturn(ZonedDateTime.now) - - service.touch(authenticator) must beLeft[SessionAuthenticator].like { - case a => - a.lastUsedDateTime must be equalTo clock.now + override def running() = { + when(settings.authenticatorIdleTimeout).thenReturn(Some(1 second)) + when(clock.now).thenReturn(ZonedDateTime.now) + + service.touch(authenticator) must beLeft[SessionAuthenticator].like { + case a => + a.lastUsedDateTime must be equalTo clock.now + } } } "do not update the last used date if idle timeout is not defined" in new WithApplication with Context { - when(settings.authenticatorIdleTimeout).thenReturn(None) - when(clock.now).thenReturn(ZonedDateTime.now) - - service.touch(authenticator) must beRight[SessionAuthenticator].like { - case a => - a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime + override def running() = { + when(settings.authenticatorIdleTimeout).thenReturn(None) + when(clock.now).thenReturn(ZonedDateTime.now) + + service.touch(authenticator) must beRight[SessionAuthenticator].like { + case a => + a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime + } } } } "The `update` method of the service" should { "update the session" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) - val result = service.update(authenticator, Results.Ok) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val data = authenticatorEncoder.encode(Json.toJson(authenticator).toString()) + val result = service.update(authenticator, Results.Ok) - status(result) must be equalTo OK - session(result).get(settings.sessionKey) should beSome(data) + status(result) must be equalTo OK + session(result).get(settings.sessionKey) should beSome(data) + } } "override existing authenticator from request" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing") - val result = service.update(authenticator, Results.Ok) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing") + val result = service.update(authenticator, Results.Ok) - unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator + unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator + } } "non authenticator related session data" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep") - val result = service.update(authenticator, Results.Ok.addingToSession( - "result-other" -> "keep")) - - unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator - session(result).get("request-other") should beSome("keep") - session(result).get("result-other") should beSome("keep") + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep") + val result = service.update(authenticator, Results.Ok.addingToSession( + "result-other" -> "keep")) + + unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator + session(result).get("request-other") should beSome("keep") + session(result).get("result-other") should beSome("keep") + } } "throws an AuthenticatorUpdateException exception if an error occurred during update" in new Context { @@ -374,49 +424,55 @@ class SessionAuthenticatorSpec extends PlaySpecification { "The `renew` method of the service" should { "renew the session" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val now = ZonedDateTime.now - val data = authenticatorEncoder.encode(Json.toJson(authenticator.copy( - lastUsedDateTime = now, - expirationDateTime = now + settings.authenticatorExpiry)).toString()) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val now = ZonedDateTime.now + val data = authenticatorEncoder.encode(Json.toJson(authenticator.copy( + lastUsedDateTime = now, + expirationDateTime = now + settings.authenticatorExpiry)).toString()) - when(settings.useFingerprinting).thenReturn(false) - when(clock.now).thenReturn(now) + when(settings.useFingerprinting).thenReturn(false) + when(clock.now).thenReturn(now) - val result = service.renew(authenticator, Results.Ok) + val result = service.renew(authenticator, Results.Ok) - session(result).get(settings.sessionKey) should beSome(data) + session(result).get(settings.sessionKey) should beSome(data) + } } "override existing authenticator from request" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing") - val now = ZonedDateTime.now + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession(settings.sessionKey -> "existing") + val now = ZonedDateTime.now - when(settings.useFingerprinting).thenReturn(false) - when(clock.now).thenReturn(now) + when(settings.useFingerprinting).thenReturn(false) + when(clock.now).thenReturn(now) - val result = service.renew(authenticator, Results.Ok) + val result = service.renew(authenticator, Results.Ok) - unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator.copy( - lastUsedDateTime = now, - expirationDateTime = now + settings.authenticatorExpiry) + unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator.copy( + lastUsedDateTime = now, + expirationDateTime = now + settings.authenticatorExpiry) + } } "non authenticator related session data" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep") - val now = ZonedDateTime.now + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep") + val now = ZonedDateTime.now - when(settings.useFingerprinting).thenReturn(false) - when(clock.now).thenReturn(now) + when(settings.useFingerprinting).thenReturn(false) + when(clock.now).thenReturn(now) - val result = service.renew(authenticator, Results.Ok.addingToSession( - "result-other" -> "keep")) + val result = service.renew(authenticator, Results.Ok.addingToSession( + "result-other" -> "keep")) - unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator.copy( - lastUsedDateTime = now, - expirationDateTime = now + settings.authenticatorExpiry) - session(result).get("request-other") should beSome("keep") - session(result).get("result-other") should beSome("keep") + unserialize(session(result).get(settings.sessionKey).get, authenticatorEncoder).get must be equalTo authenticator.copy( + lastUsedDateTime = now, + expirationDateTime = now + settings.authenticatorExpiry) + session(result).get("request-other") should beSome("keep") + session(result).get("result-other") should beSome("keep") + } } "throws an AuthenticatorRenewalException exception if an error occurred during renewal" in new Context { @@ -437,32 +493,38 @@ class SessionAuthenticatorSpec extends PlaySpecification { "The `discard` method of the service" should { "discard the authenticator from session" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val result = service.discard(authenticator, Results.Ok.withSession( - settings.sessionKey -> "test")) + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val result = service.discard(authenticator, Results.Ok.withSession( + settings.sessionKey -> "test")) - session(result).get(settings.sessionKey) should beNone + session(result).get(settings.sessionKey) should beNone + } } "non authenticator related session data" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep", settings.sessionKey -> "test") - val result = service.discard(authenticator, Results.Ok.addingToSession( - "result-other" -> "keep")) - - session(result).get(settings.sessionKey) should beNone - session(result).get("request-other") should beSome("keep") - session(result).get("result-other") should beSome("keep") + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withSession("request-other" -> "keep", settings.sessionKey -> "test") + val result = service.discard(authenticator, Results.Ok.addingToSession( + "result-other" -> "keep")) + + session(result).get(settings.sessionKey) should beNone + session(result).get("request-other") should beSome("keep") + session(result).get("result-other") should beSome("keep") + } } "throws an AuthenticatorDiscardingException exception if an error occurred during discarding" in new WithApplication with Context { - implicit val request: FakeRequest[AnyContentAsEmpty.type] = spy(FakeRequest()).withSession(settings.sessionKey -> "test") - val result = mock[Result] + override def running() = { + implicit val request: FakeRequest[AnyContentAsEmpty.type] = spy(FakeRequest()).withSession(settings.sessionKey -> "test") + val result = mock[Result] - when(result.removingFromSession(any)(any)).thenThrow(new RuntimeException("Cannot get session")) + when(result.removingFromSession(any)(any)).thenThrow(new RuntimeException("Cannot get session")) - await(service.discard(authenticator, result)) must throwA[AuthenticatorDiscardingException].like { - case e => - e.getMessage must startWith(DiscardError.format(ID, "")) + await(service.discard(authenticator, result)) must throwA[AuthenticatorDiscardingException].like { + case e => + e.getMessage must startWith(DiscardError.format(ID, "")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/BasicAuthProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/BasicAuthProviderSpec.scala index 7ada25fb..5a138f4a 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/BasicAuthProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/BasicAuthProviderSpec.scala @@ -33,103 +33,123 @@ class BasicAuthProviderSpec extends PasswordProviderSpec { "The `authenticate` method" should { "throw ConfigurationException if unsupported hasher is stored" in new WithApplication with Context { - val passwordInfo = PasswordInfo("unknown", "hashed(s3cr3t)") - val loginInfo = LoginInfo(provider.id, credentials.identifier) - val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) + override def running() = { + val passwordInfo = PasswordInfo("unknown", "hashed(s3cr3t)") + val loginInfo = LoginInfo(provider.id, credentials.identifier) + val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) - await(provider.authenticate(request)) must throwA[ConfigurationException].like { - case e => e.getMessage must beEqualTo(HasherIsNotRegistered.format(provider.id, "unknown", "foo, bar")) + await(provider.authenticate(request)) must throwA[ConfigurationException].like { + case e => e.getMessage must beEqualTo(HasherIsNotRegistered.format(provider.id, "unknown", "foo, bar")) + } } } "return None if no auth info could be found for the given credentials" in new WithApplication with Context { - val loginInfo = new LoginInfo(provider.id, credentials.identifier) - val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) + override def running() = { + val loginInfo = new LoginInfo(provider.id, credentials.identifier) + val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(None)) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(None)) - await(provider.authenticate(request)) must beNone + await(provider.authenticate(request)) must beNone + } } "return None if password does not match" in new WithApplication with Context { - val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") - val loginInfo = LoginInfo(provider.id, credentials.identifier) - val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) + override def running() = { + val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") + val loginInfo = LoginInfo(provider.id, credentials.identifier) + val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) - when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(false) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(false) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) - await(provider.authenticate(request)) must beNone + await(provider.authenticate(request)) must beNone + } } "return None if provider isn't responsible" in new WithApplication with Context { - await(provider.authenticate(FakeRequest())) must beNone + override def running() = { + await(provider.authenticate(FakeRequest())) must beNone + } } "return None for wrong encoded credentials" in new WithApplication with Context { - val request = FakeRequest().withHeaders(AUTHORIZATION -> "wrong") + override def running() = { + val request = FakeRequest().withHeaders(AUTHORIZATION -> "wrong") - await(provider.authenticate(request)) must beNone + await(provider.authenticate(request)) must beNone + } } "return login info if passwords does match" in new WithApplication with Context { - val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") - val loginInfo = LoginInfo(provider.id, credentials.identifier) - val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) + override def running() = { + val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") + val loginInfo = LoginInfo(provider.id, credentials.identifier) + val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) - when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) - await(provider.authenticate(request)) must beSome(loginInfo) + await(provider.authenticate(request)) must beSome(loginInfo) + } } "handle a colon in a password" in new WithApplication with Context { - val credentialsWithColon = Credentials("apollonia.vanova@watchmen.com", "s3c:r3t") - val passwordInfo = PasswordInfo("foo", "hashed(s3c:r3t)") - val loginInfo = LoginInfo(provider.id, credentialsWithColon.identifier) - val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentialsWithColon)) + override def running() = { + val credentialsWithColon = Credentials("apollonia.vanova@watchmen.com", "s3c:r3t") + val passwordInfo = PasswordInfo("foo", "hashed(s3c:r3t)") + val loginInfo = LoginInfo(provider.id, credentialsWithColon.identifier) + val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentialsWithColon)) - when(fooHasher.matches(passwordInfo, credentialsWithColon.password)).thenReturn(true) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(fooHasher.matches(passwordInfo, credentialsWithColon.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) - await(provider.authenticate(request)) must beSome(loginInfo) + await(provider.authenticate(request)) must beSome(loginInfo) + } } "re-hash password with new hasher if hasher is deprecated" in new WithApplication with Context { - val passwordInfo = PasswordInfo("bar", "hashed(s3cr3t)") - val loginInfo = LoginInfo(provider.id, credentials.identifier) - val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) - - when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo) - when(barHasher.matches(passwordInfo, credentials.password)).thenReturn(true) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) - when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo)) - - await(provider.authenticate(request)) must beSome(loginInfo) - verify(authInfoRepository).update(loginInfo, passwordInfo) + override def running() = { + val passwordInfo = PasswordInfo("bar", "hashed(s3cr3t)") + val loginInfo = LoginInfo(provider.id, credentials.identifier) + val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) + + when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo) + when(barHasher.matches(passwordInfo, credentials.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo)) + + await(provider.authenticate(request)) must beSome(loginInfo) + verify(authInfoRepository).update(loginInfo, passwordInfo) + } } "re-hash password with new hasher if password info is deprecated" in new WithApplication with Context { - val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") - val loginInfo = LoginInfo(provider.id, credentials.identifier) - val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) - - when(fooHasher.isDeprecated(passwordInfo)).thenReturn(Some(true)) - when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo) - when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) - when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo)) - - await(provider.authenticate(request)) must beSome(loginInfo) - verify(authInfoRepository).update(loginInfo, passwordInfo) + override def running() = { + val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") + val loginInfo = LoginInfo(provider.id, credentials.identifier) + val request = FakeRequest().withHeaders(AUTHORIZATION -> encodeCredentials(credentials)) + + when(fooHasher.isDeprecated(passwordInfo)).thenReturn(Some(true)) + when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo) + when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo)) + + await(provider.authenticate(request)) must beSome(loginInfo) + verify(authInfoRepository).update(loginInfo, passwordInfo) + } } "return None if Authorization method is not Basic and Base64 decoded header has ':'" in new WithApplication with Context { - val request = FakeRequest().withHeaders(AUTHORIZATION -> Base64.encode("NotBasic foo:bar")) + override def running() = { + val request = FakeRequest().withHeaders(AUTHORIZATION -> Base64.encode("NotBasic foo:bar")) - await(provider.authenticate(request)) must beNone + await(provider.authenticate(request)) must beNone + } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/CredentialsProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/CredentialsProviderSpec.scala index 515f008b..72065b2d 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/CredentialsProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/CredentialsProviderSpec.scala @@ -33,73 +33,85 @@ class CredentialsProviderSpec extends PasswordProviderSpec { "The `authenticate` method" should { "throw IdentityNotFoundException if no auth info could be found for the given credentials" in new WithApplication with Context { - val loginInfo = new LoginInfo(provider.id, credentials.identifier) + override def running() = { + val loginInfo = new LoginInfo(provider.id, credentials.identifier) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(None)) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(None)) - await(provider.authenticate(credentials)) must throwA[IdentityNotFoundException].like { - case e => e.getMessage must beEqualTo(PasswordInfoNotFound.format(provider.id, loginInfo)) + await(provider.authenticate(credentials)) must throwA[IdentityNotFoundException].like { + case e => e.getMessage must beEqualTo(PasswordInfoNotFound.format(provider.id, loginInfo)) + } } } "throw InvalidPasswordException if password does not match" in new WithApplication with Context { - val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") - val loginInfo = LoginInfo(provider.id, credentials.identifier) + override def running() = { + val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") + val loginInfo = LoginInfo(provider.id, credentials.identifier) - when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(false) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(false) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) - await(provider.authenticate(credentials)) must throwA[InvalidPasswordException].like { - case e => e.getMessage must beEqualTo(PasswordDoesNotMatch.format(provider.id)) + await(provider.authenticate(credentials)) must throwA[InvalidPasswordException].like { + case e => e.getMessage must beEqualTo(PasswordDoesNotMatch.format(provider.id)) + } } } "throw ConfigurationException if unsupported hasher is stored" in new WithApplication with Context { - val passwordInfo = PasswordInfo("unknown", "hashed(s3cr3t)") - val loginInfo = LoginInfo(provider.id, credentials.identifier) + override def running() = { + val passwordInfo = PasswordInfo("unknown", "hashed(s3cr3t)") + val loginInfo = LoginInfo(provider.id, credentials.identifier) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) - await(provider.authenticate(credentials)) must throwA[ConfigurationException].like { - case e => e.getMessage must beEqualTo(HasherIsNotRegistered.format(provider.id, "unknown", "foo, bar")) + await(provider.authenticate(credentials)) must throwA[ConfigurationException].like { + case e => e.getMessage must beEqualTo(HasherIsNotRegistered.format(provider.id, "unknown", "foo, bar")) + } } } "return login info if passwords does match" in new WithApplication with Context { - val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") - val loginInfo = LoginInfo(provider.id, credentials.identifier) + override def running() = { + val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") + val loginInfo = LoginInfo(provider.id, credentials.identifier) - when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) - await(provider.authenticate(credentials)) must be equalTo loginInfo + await(provider.authenticate(credentials)) must be equalTo loginInfo + } } "re-hash password with new hasher if hasher is deprecated" in new WithApplication with Context { - val passwordInfo = PasswordInfo("bar", "hashed(s3cr3t)") - val loginInfo = LoginInfo(provider.id, credentials.identifier) + override def running() = { + val passwordInfo = PasswordInfo("bar", "hashed(s3cr3t)") + val loginInfo = LoginInfo(provider.id, credentials.identifier) - when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo) - when(barHasher.matches(passwordInfo, credentials.password)).thenReturn(true) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) - when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo)) + when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo) + when(barHasher.matches(passwordInfo, credentials.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo)) - await(provider.authenticate(credentials)) must be equalTo loginInfo - verify(authInfoRepository).update(loginInfo, passwordInfo) + await(provider.authenticate(credentials)) must be equalTo loginInfo + verify(authInfoRepository).update(loginInfo, passwordInfo) + } } "re-hash password with new hasher if hasher is deprecated" in new WithApplication with Context { - val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") - val loginInfo = LoginInfo(provider.id, credentials.identifier) - - when(fooHasher.isDeprecated(passwordInfo)).thenReturn(Some(true)) - when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo) - when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true) - when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) - when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo)) - - await(provider.authenticate(credentials)) must be equalTo loginInfo - verify(authInfoRepository).update(loginInfo, passwordInfo) + override def running() = { + val passwordInfo = PasswordInfo("foo", "hashed(s3cr3t)") + val loginInfo = LoginInfo(provider.id, credentials.identifier) + + when(fooHasher.isDeprecated(passwordInfo)).thenReturn(Some(true)) + when(fooHasher.hash(credentials.password)).thenReturn(passwordInfo) + when(fooHasher.matches(passwordInfo, credentials.password)).thenReturn(true) + when(authInfoRepository.find[PasswordInfo](loginInfo)).thenReturn(Future.successful(Some(passwordInfo))) + when(authInfoRepository.update[PasswordInfo](loginInfo, passwordInfo)).thenReturn(Future.successful(passwordInfo)) + + await(provider.authenticate(credentials)) must be equalTo loginInfo + verify(authInfoRepository).update(loginInfo, passwordInfo) + } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala index 6896921a..3228ce6e 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth1ProviderSpec.scala @@ -45,52 +45,64 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] { "The provider" should { val c = context "throw a RuntimeException if the unsafe 1.0 specification should be used" in new WithApplication { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Denied + "=") - when(c.oAuthService.use10a).thenReturn(false) - c.provider.authenticate() must throwA[RuntimeException] + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Denied + "=") + when(c.oAuthService.use10a).thenReturn(false) + c.provider.authenticate() must throwA[RuntimeException] + } } } "The authenticate method" should { val c = context "fail with an AccessDeniedException if denied key exists in query string" in new WithApplication { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Denied + "=") - failed[AccessDeniedException](c.provider.authenticate()) { - case e => e.getMessage must startWith(AuthorizationError.format(c.provider.id, "")) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Denied + "=") + failed[AccessDeniedException](c.provider.authenticate()) { + case e => e.getMessage must startWith(AuthorizationError.format(c.provider.id, "")) + } } } "fail with an UnexpectedResponseException if request token cannot be retrieved" in new WithApplication { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - when(c.oAuthService.retrieveRequestToken(c.oAuthSettings.callbackURL)).thenReturn(Future.failed(new Exception(""))) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + when(c.oAuthService.retrieveRequestToken(c.oAuthSettings.callbackURL)).thenReturn(Future.failed(new Exception(""))) - failed[UnexpectedResponseException](c.provider.authenticate()) { - case e => e.getMessage must startWith(ErrorRequestToken.format(c.provider.id, "")) + failed[UnexpectedResponseException](c.provider.authenticate()) { + case e => e.getMessage must startWith(ErrorRequestToken.format(c.provider.id, "")) + } } } "redirect to authorization URL if request token could be retrieved" in new WithApplication { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val serializedTokenSecret = "my.serialized.token.secret" - - when(c.oAuthService.retrieveRequestToken(c.oAuthSettings.callbackURL)).thenReturn(Future.successful(c.oAuthInfo)) - when(c.oAuthService.redirectUrl(any())).thenAnswer(_ => c.oAuthSettings.authorizationURL) - when(c.oAuthTokenSecretProvider.build(any())(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret)) - // when(c.oAuthTokenSecretProvider.publish(any(), any())(any())).thenAnswer(_.getArguments.asInstanceOf[Array[Any]](0).asInstanceOf[Result]) - when(c.oAuthTokenSecretProvider.publish(any(), any())(any())).thenAnswer(_.getArgument(0).asInstanceOf[Result]) - - result(c.provider.authenticate()) { result => - status(result) must equalTo(SEE_OTHER) - redirectLocation(result) must beSome[String].which(_ == c.oAuthSettings.authorizationURL) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val serializedTokenSecret = "my.serialized.token.secret" + + when(c.oAuthService.retrieveRequestToken(c.oAuthSettings.callbackURL)).thenReturn(Future.successful(c.oAuthInfo)) + when(c.oAuthService.redirectUrl(any())).thenAnswer(_ => c.oAuthSettings.authorizationURL) + when(c.oAuthTokenSecretProvider.build(any())(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret)) + // when(c.oAuthTokenSecretProvider.publish(any(), any())(any())).thenAnswer(_.getArguments.asInstanceOf[Array[Any]](0).asInstanceOf[Result]) + when(c.oAuthTokenSecretProvider.publish(any(), any())(any())).thenAnswer(_.getArgument(0).asInstanceOf[Result]) + + result(c.provider.authenticate()) { result => + status(result) must equalTo(SEE_OTHER) + redirectLocation(result) must beSome[String].which(_ == c.oAuthSettings.authorizationURL) + } } } "resolves relative redirectURLs before starting the flow" in new WithApplication { - verifyCallbackURLResolution("/callback-url", secure = false, "http://www.example.com/callback-url") + override def running() = { + verifyCallbackURLResolution("/callback-url", secure = false, "http://www.example.com/callback-url") + } } "resolves path relative redirectURLS before starting the flow" in new WithApplication { - verifyCallbackURLResolution("callback-url", secure = false, "http://www.example.com/request-path/callback-url") + override def running() = { + verifyCallbackURLResolution("callback-url", secure = false, "http://www.example.com/request-path/callback-url") + } } def verifyCallbackURLResolution(callbackURL: String, secure: Boolean, resolvedCallbackURL: String) = { @@ -115,31 +127,37 @@ abstract class OAuth1ProviderSpec extends SocialProviderSpec[OAuth1Info] { } "resolves relative redirectURLs before starting the flow over https" in new WithApplication { - verifyCallbackURLResolution("/callback-url", secure = true, "https://www.example.com/callback-url") + override def running() = { + verifyCallbackURLResolution("/callback-url", secure = true, "https://www.example.com/callback-url") + } } "fail with an UnexpectedResponseException if access token cannot be retrieved" in new WithApplication { - val tokenSecret = "my.token.secret" - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + OAuthVerifier + "=my.verifier&" + OAuthToken + "=my.token") + override def running() = { + val tokenSecret = "my.token.secret" + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + OAuthVerifier + "=my.verifier&" + OAuthToken + "=my.token") - when(c.oAuthTokenSecret.value).thenReturn(tokenSecret) - when(c.oAuthTokenSecretProvider.retrieve(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret)) - when(c.oAuthService.retrieveAccessToken(c.oAuthInfo.copy(secret = tokenSecret), "my.verifier")).thenReturn(Future.failed(new Exception(""))) + when(c.oAuthTokenSecret.value).thenReturn(tokenSecret) + when(c.oAuthTokenSecretProvider.retrieve(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret)) + when(c.oAuthService.retrieveAccessToken(c.oAuthInfo.copy(secret = tokenSecret), "my.verifier")).thenReturn(Future.failed(new Exception(""))) - failed[UnexpectedResponseException](c.provider.authenticate()) { - case e => e.getMessage must startWith(ErrorAccessToken.format(c.provider.id, "")) + failed[UnexpectedResponseException](c.provider.authenticate()) { + case e => e.getMessage must startWith(ErrorAccessToken.format(c.provider.id, "")) + } } } "return the auth info" in new WithApplication { - val tokenSecret = "my.token.secret" - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + OAuthVerifier + "=my.verifier&" + OAuthToken + "=my.token") + override def running() = { + val tokenSecret = "my.token.secret" + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + OAuthVerifier + "=my.verifier&" + OAuthToken + "=my.token") - when(c.oAuthTokenSecret.value).thenReturn(tokenSecret) - when(c.oAuthTokenSecretProvider.retrieve(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret)) - when(c.oAuthService.retrieveAccessToken(c.oAuthInfo.copy(secret = tokenSecret), "my.verifier")).thenReturn(Future.successful(c.oAuthInfo)) + when(c.oAuthTokenSecret.value).thenReturn(tokenSecret) + when(c.oAuthTokenSecretProvider.retrieve(any(), any())).thenReturn(Future.successful(c.oAuthTokenSecret)) + when(c.oAuthService.retrieveAccessToken(c.oAuthInfo.copy(secret = tokenSecret), "my.verifier")).thenReturn(Future.successful(c.oAuthInfo)) - authInfo(c.provider.authenticate())(_ must be equalTo c.oAuthInfo) + authInfo(c.provider.authenticate())(_ must be equalTo c.oAuthInfo) + } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala index 861d521b..f93dc674 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OAuth2ProviderSpec.scala @@ -48,103 +48,125 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So "The `authenticate` method" should { val c = context "fail with an AccessDeniedException if `error` key with value `access_denied` exists in query string" in new WithApplication { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Error + "=" + AccessDenied) - failed[AccessDeniedException](c.provider.authenticate()) { - case e => e.getMessage must startWith(AuthorizationError.format(c.provider.id, "")) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Error + "=" + AccessDenied) + failed[AccessDeniedException](c.provider.authenticate()) { + case e => e.getMessage must startWith(AuthorizationError.format(c.provider.id, "")) + } } } "fail with an UnexpectedResponseException if `error` key with unspecified value exists in query string" in new WithApplication { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Error + "=unspecified") - failed[UnexpectedResponseException](c.provider.authenticate()) { - case e => e.getMessage must startWith(AuthorizationError.format(c.provider.id, "unspecified")) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Error + "=unspecified") + failed[UnexpectedResponseException](c.provider.authenticate()) { + case e => e.getMessage must startWith(AuthorizationError.format(c.provider.id, "unspecified")) + } } } "fail with an ConfigurationException if authorization URL is undefined when it's needed" in new WithApplication { - c.oAuthSettings.authorizationURL match { - case None => skipped("authorizationURL is not defined, so this step isn't needed for provider: " + c.provider.getClass) - case Some(_) => - implicit val req = FakeRequest(GET, "/") - - when(c.stateProvider.serialize(c.state)).thenReturn("session-value") - when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) - when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) - when(c.oAuthSettings.authorizationURL).thenReturn(None) - - failed[ConfigurationException](c.provider.authenticate()) { - case e => e.getMessage must startWith(AuthorizationURLUndefined.format(c.provider.id)) - } + override def running() = { + c.oAuthSettings.authorizationURL match { + case None => skipped("authorizationURL is not defined, so this step isn't needed for provider: " + c.provider.getClass) + case Some(_) => + implicit val req = FakeRequest(GET, "/") + + when(c.stateProvider.serialize(c.state)).thenReturn("session-value") + when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.oAuthSettings.authorizationURL).thenReturn(None) + + failed[ConfigurationException](c.provider.authenticate()) { + case e => e.getMessage must startWith(AuthorizationURLUndefined.format(c.provider.id)) + } + } } } "redirect to authorization URL if authorization code doesn't exists in request" in new WithApplication { - c.oAuthSettings.authorizationURL match { - case None => skipped("authorizationURL is not defined, so this step isn't needed for provider: " + c.provider.getClass) - case Some(authorizationURL) => - implicit val req = FakeRequest(GET, "/") - val sessionKey = "session-key" - val sessionValue = "session-value" - - when(c.stateProvider.serialize(c.state)).thenReturn(sessionValue) - when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) - when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) - when(c.stateProvider.publish(any, any)(any)).thenAnswer { m => - val result = m.getArgument(0).asInstanceOf[Result] - val state = m.getArgument(1).asInstanceOf[c.TestState] - - result.withSession(sessionKey -> c.stateProvider.serialize(state)) - } + override def running() = { + c.oAuthSettings.authorizationURL match { + case None => skipped("authorizationURL is not defined, so this step isn't needed for provider: " + c.provider.getClass) + case Some(authorizationURL) => + implicit val req = FakeRequest(GET, "/") + val sessionKey = "session-key" + val sessionValue = "session-value" + + when(c.stateProvider.serialize(c.state)).thenReturn(sessionValue) + when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.publish(any, any)(any)).thenAnswer { m => + val result = m.getArgument(0).asInstanceOf[Result] + val state = m.getArgument(1).asInstanceOf[c.TestState] + + result.withSession(sessionKey -> c.stateProvider.serialize(state)) + } - result(c.provider.authenticate()) { result => - status(result) must equalTo(SEE_OTHER) - session(result).get(sessionKey) must beSome(c.stateProvider.serialize(c.state)) - redirectLocation(result) must beSome[String].which { url => - val urlParams = c.urlParams(url) - val redirectParam = c.oAuthSettings.redirectURL match { - case Some(rUri) => List((RedirectURI, rUri)) - case None => Nil - } - val params = c.oAuthSettings.scope.foldLeft(List( - (ClientID, c.oAuthSettings.clientID), - (ResponseType, Code), - (State, urlParams(State))) ++ c.oAuthSettings.authorizationParams.toList ++ redirectParam) { - case (p, s) => (Scope, s) :: p + result(c.provider.authenticate()) { result => + status(result) must equalTo(SEE_OTHER) + session(result).get(sessionKey) must beSome(c.stateProvider.serialize(c.state)) + redirectLocation(result) must beSome[String].which { url => + val urlParams = c.urlParams(url) + val redirectParam = c.oAuthSettings.redirectURL match { + case Some(rUri) => List((RedirectURI, rUri)) + case None => Nil + } + val params = c.oAuthSettings.scope.foldLeft(List( + (ClientID, c.oAuthSettings.clientID), + (ResponseType, Code), + (State, urlParams(State))) ++ c.oAuthSettings.authorizationParams.toList ++ redirectParam) { + case (p, s) => (Scope, s) :: p + } + url must be equalTo (authorizationURL + params.map { p => + encode(p._1, "UTF-8") + "=" + encode(p._2, "UTF-8") + }.mkString("?", "&", "")) } - url must be equalTo (authorizationURL + params.map { p => - encode(p._1, "UTF-8") + "=" + encode(p._2, "UTF-8") - }.mkString("?", "&", "")) } - } + } } } "resolves relative redirectURLs before starting the flow" in new WithApplication { - verifyRelativeRedirectResolution("/redirect-url", secure = false, "http://www.example.com/redirect-url") + override def running() = { + verifyRelativeRedirectResolution("/redirect-url", secure = false, "http://www.example.com/redirect-url") + } } "resolves path relative redirectURLs before starting the flow" in new WithApplication { - verifyRelativeRedirectResolution("redirect-url", secure = false, "http://www.example.com/request-path/redirect-url") + override def running() = { + verifyRelativeRedirectResolution("redirect-url", secure = false, "http://www.example.com/request-path/redirect-url") + } } "resolves relative redirectURLs before starting the flow over https" in new WithApplication { - verifyRelativeRedirectResolution("/redirect-url", secure = true, "https://www.example.com/redirect-url") + override def running() = { + verifyRelativeRedirectResolution("/redirect-url", secure = true, "https://www.example.com/redirect-url") + } } "verifying presence of redirect param in the access token post request" in new WithApplication { - verifyPresenceOrAbsenceOfRedirectURL(Some("/redirect-url"), secure = false, "http://www.example.com/redirect-url") + override def running() = { + verifyPresenceOrAbsenceOfRedirectURL(Some("/redirect-url"), secure = false, "http://www.example.com/redirect-url") + } } "verifying presence of redirect param in the access token post request over https" in new WithApplication { - verifyPresenceOrAbsenceOfRedirectURL(Some("/redirect-url"), secure = true, "https://www.example.com/redirect-url") + override def running() = { + verifyPresenceOrAbsenceOfRedirectURL(Some("/redirect-url"), secure = true, "https://www.example.com/redirect-url") + } } "verifying absence of redirect param in the access token post request" in new WithApplication { - verifyPresenceOrAbsenceOfRedirectURL(None, secure = false, "http://www.example.com/request-path/redirect-url") + override def running() = { + verifyPresenceOrAbsenceOfRedirectURL(None, secure = false, "http://www.example.com/request-path/redirect-url") + } } "verifying absence of redirect param in the access token post request over https" in new WithApplication { - verifyPresenceOrAbsenceOfRedirectURL(None, secure = true, "https://www.example.com/redirect-url") + override def running() = { + verifyPresenceOrAbsenceOfRedirectURL(None, secure = true, "https://www.example.com/redirect-url") + } } def verifyRelativeRedirectResolution(redirectURL: String, secure: Boolean, resolvedRedirectURL: String) = { @@ -223,76 +245,82 @@ abstract class OAuth2ProviderSpec extends SocialStateProviderSpec[OAuth2Info, So } "not send state param if state is empty" in new WithApplication { - c.oAuthSettings.authorizationURL match { - case None => skipped("authorizationURL is not defined, so this step isn't needed for provider: " + c.provider.getClass) - case Some(_) => - implicit val req = FakeRequest(GET, "/") - - when(c.stateProvider.serialize(c.state)).thenReturn("") - when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) - when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) - when(c.stateProvider.publish(any, any)(any)).thenAnswer { m => - m.getArgument(0).asInstanceOf[Result] - } + override def running() = { + c.oAuthSettings.authorizationURL match { + case None => skipped("authorizationURL is not defined, so this step isn't needed for provider: " + c.provider.getClass) + case Some(_) => + implicit val req = FakeRequest(GET, "/") + + when(c.stateProvider.serialize(c.state)).thenReturn("") + when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.publish(any, any)(any)).thenAnswer { m => + m.getArgument(0).asInstanceOf[Result] + } - result(c.provider.authenticate())(result => - redirectLocation(result) must beSome[String].which(_ must not contain State)) + result(c.provider.authenticate())(result => + redirectLocation(result) must beSome[String].which(_ must not contain State)) + } } } "submit the proper params to the access token post request" in new WithApplication { - val wsRequest = mock[MockWSRequest] - val redirectParam = c.oAuthSettings.redirectURL match { - case Some(rUri) => - List((RedirectURI, rUri)) - case None => Nil - } - val params = Map( - ClientID -> Seq(c.oAuthSettings.clientID), - ClientSecret -> Seq(c.oAuthSettings.clientSecret), - GrantType -> Seq(AuthorizationCode), - Code -> Seq("my.code")) ++ c.oAuthSettings.accessTokenParams.transformValues(Seq(_)) ++ redirectParam.toMap.transformValues(Seq(_)) - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - - // We must use this neat trick here because it isn't possible to check the post call with a verification, - // because of the implicit params needed for the post call. On the other hand we can test it in the abstract - // spec, because we throw an exception in both cases which stops the test once the post method was called. - // This protects as for an NPE because of the not mocked dependencies. The other solution would be to execute - // this test in every provider with the full mocked dependencies. - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenAnswer { m => - if (m.getArgument(0).asInstanceOf[Map[String, Seq[String]]].equals(params)) { - throw new RuntimeException("success") - } else { - throw new RuntimeException("failure") + override def running() = { + val wsRequest = mock[MockWSRequest] + val redirectParam = c.oAuthSettings.redirectURL match { + case Some(rUri) => + List((RedirectURI, rUri)) + case None => Nil } - } - when(c.httpLayer.url(c.oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) - when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) + val params = Map( + ClientID -> Seq(c.oAuthSettings.clientID), + ClientSecret -> Seq(c.oAuthSettings.clientSecret), + GrantType -> Seq(AuthorizationCode), + Code -> Seq("my.code")) ++ c.oAuthSettings.accessTokenParams.transformValues(Seq(_)) ++ redirectParam.toMap.transformValues(Seq(_)) + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + + // We must use this neat trick here because it isn't possible to check the post call with a verification, + // because of the implicit params needed for the post call. On the other hand we can test it in the abstract + // spec, because we throw an exception in both cases which stops the test once the post method was called. + // This protects as for an NPE because of the not mocked dependencies. The other solution would be to execute + // this test in every provider with the full mocked dependencies. + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenAnswer { m => + if (m.getArgument(0).asInstanceOf[Map[String, Seq[String]]].equals(params)) { + throw new RuntimeException("success") + } else { + throw new RuntimeException("failure") + } + } + when(c.httpLayer.url(c.oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) - failed[RuntimeException](c.provider.authenticate()) { - case e => e.getMessage must startWith("success") + failed[RuntimeException](c.provider.authenticate()) { + case e => e.getMessage must startWith("success") + } } } "fail with UnexpectedResponseException if Json cannot be parsed from response" in new WithApplication { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenThrow(new RuntimeException("Unexpected character ('<' (code 60))")) - when(wsResponse.body).thenReturn("") - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(c.httpLayer.url(c.oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) - when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) - - failed[UnexpectedResponseException](c.provider.authenticate()) { - case e => e.getMessage must startWith( - JsonParseError.format(c.provider.id, "", "java.lang.RuntimeException: Unexpected character ('<' (code 60))")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenThrow(new RuntimeException("Unexpected character ('<' (code 60))")) + when(wsResponse.body).thenReturn("") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(c.httpLayer.url(c.oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(c.stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(c.state)) + when(c.stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(c.state)) + + failed[UnexpectedResponseException](c.provider.authenticate()) { + case e => e.getMessage must startWith( + JsonParseError.format(c.provider.id, "", "java.lang.RuntimeException: Unexpected character ('<' (code 60))")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala index f27ed5fd..f31a7430 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/OpenIDProviderSpec.scala @@ -43,45 +43,57 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] { "The authenticate method" should { val c = context "fail with an UnexpectedResponseException if redirect URL couldn't be retrieved" in new WithApplication { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - when(c.openIDService.redirectURL(any(), any())(any())).thenReturn(Future.failed(new Exception(""))) + when(c.openIDService.redirectURL(any(), any())(any())).thenReturn(Future.failed(new Exception(""))) - failed[UnexpectedResponseException](c.provider.authenticate()) { - case e => e.getMessage must startWith(ErrorRedirectURL.format(c.provider.id, "")) + failed[UnexpectedResponseException](c.provider.authenticate()) { + case e => e.getMessage must startWith(ErrorRedirectURL.format(c.provider.id, "")) + } } } "redirect to provider by using the provider URL" in new WithApplication { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - when(c.openIDService.redirectURL(any(), any())(any())).thenAnswer(_ => Future.successful(c.openIDSettings.providerURL)) - - result(c.provider.authenticate()) { result => - status(result) must equalTo(SEE_OTHER) - redirectLocation(result) must beSome[String].which(_ == c.openIDSettings.providerURL) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + when(c.openIDService.redirectURL(any(), any())(any())).thenAnswer(_ => Future.successful(c.openIDSettings.providerURL)) + + result(c.provider.authenticate()) { result => + status(result) must equalTo(SEE_OTHER) + redirectLocation(result) must beSome[String].which(_ == c.openIDSettings.providerURL) + } } } "redirect to provider by using a openID" in new WithApplication { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?openID=my.open.id") - when(c.openIDService.redirectURL(any(), any())(any())).thenAnswer(_ => Future.successful(c.openIDSettings.providerURL)) - - result(c.provider.authenticate()) { result => - status(result) must equalTo(SEE_OTHER) - redirectLocation(result) must beSome[String].which(_ == c.openIDSettings.providerURL) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?openID=my.open.id") + when(c.openIDService.redirectURL(any(), any())(any())).thenAnswer(_ => Future.successful(c.openIDSettings.providerURL)) + + result(c.provider.authenticate()) { result => + status(result) must equalTo(SEE_OTHER) + redirectLocation(result) must beSome[String].which(_ == c.openIDSettings.providerURL) + } } } "resolves relative callbackURLs before starting the flow" in new WithApplication { - verifyRelativeCallbackURLResolution("/callback-url", secure = false, "http://www.example.com/callback-url") + override def running() = { + verifyRelativeCallbackURLResolution("/callback-url", secure = false, "http://www.example.com/callback-url") + } } "resolves path relative callbackURLs before starting the flow" in new WithApplication { - verifyRelativeCallbackURLResolution("callback-url", secure = false, "http://www.example.com/request-path/callback-url") + override def running() = { + verifyRelativeCallbackURLResolution("callback-url", secure = false, "http://www.example.com/request-path/callback-url") + } } "resolves relative callbackURLs before starting the flow over https" in new WithApplication { - verifyRelativeCallbackURLResolution("/callback-url", secure = true, "https://www.example.com/callback-url") + override def running() = { + verifyRelativeCallbackURLResolution("/callback-url", secure = true, "https://www.example.com/callback-url") + } } def verifyRelativeCallbackURLResolution(callbackURL: String, secure: Boolean, resolvedCallbackURL: String) = { @@ -102,19 +114,23 @@ abstract class OpenIDProviderSpec extends SocialProviderSpec[OpenIDInfo] { } "fail with an UnexpectedResponseException if auth info cannot be retrieved" in new WithApplication { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Mode + "=id_res") - when(c.openIDService.verifiedID(any(), any())).thenReturn(Future.failed(new Exception(""))) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Mode + "=id_res") + when(c.openIDService.verifiedID(any(), any())).thenReturn(Future.failed(new Exception(""))) - failed[UnexpectedResponseException](c.provider.authenticate()) { - case e => e.getMessage must startWith(ErrorVerification.format(c.provider.id, "")) + failed[UnexpectedResponseException](c.provider.authenticate()) { + case e => e.getMessage must startWith(ErrorVerification.format(c.provider.id, "")) + } } } "return the auth info" in new WithApplication { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Mode + "=id_res") - when(c.openIDService.verifiedID(any(), any())).thenAnswer(_ => Future.successful(c.openIDInfo)) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Mode + "=id_res") + when(c.openIDService.verifiedID(any(), any())).thenAnswer(_ => Future.successful(c.openIDInfo)) - authInfo(c.provider.authenticate())(_ must be equalTo c.openIDInfo) + authInfo(c.provider.authenticate())(_ must be equalTo c.openIDInfo) + } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala index fad9a93d..c60b1fb9 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala @@ -41,113 +41,127 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s: CustomFacebookProvider = provider.withSettings { s => - s.copy(accessTokenURL = "new-access-token-url") - } + override def running() = { + val s: CustomFacebookProvider = provider.withSettings { s => + s.copy(accessTokenURL = "new-access-token-url") + } - s.settings.accessTokenURL must be equalTo "new-access-token-url" + s.settings.accessTokenURL must be equalTo "new-access-token-url" + } } } "The `authenticate` method" should { "fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context { - val wsRequest = mock(classOf[MockWSRequest]) - val wsResponse = mock(classOf[MockWSRequest#Response]) - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(401) - when(wsResponse.body).thenReturn("Unauthorized") - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + override def running() = { + val wsRequest = mock(classOf[MockWSRequest]) + val wsResponse = mock(classOf[MockWSRequest#Response]) + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + } } } "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { - val wsRequest = mock(classOf[MockWSRequest]) - val wsResponse = mock(classOf[MockWSRequest#Response]) - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Json.obj()) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + override def running() = { + val wsRequest = mock(classOf[MockWSRequest]) + val wsResponse = mock(classOf[MockWSRequest#Response]) + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + } } } "return the auth info" in new WithApplication with Context { - val wsRequest = mock(classOf[MockWSRequest]) - val wsResponse = mock(classOf[MockWSRequest#Response]) - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + override def running() = { + val wsRequest = mock(classOf[MockWSRequest]) + val wsResponse = mock(classOf[MockWSRequest#Response]) + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + } } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock(classOf[MockWSRequest]) - val wsResponse = mock(classOf[MockWSRequest#Response]) - when(wsResponse.status).thenReturn(400) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/custom/facebook.error.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + override def running() = { + val wsRequest = mock(classOf[MockWSRequest]) + val wsResponse = mock(classOf[MockWSRequest#Response]) + when(wsResponse.status).thenReturn(400) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/custom/facebook.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - "An active access token must be used to query information about the current user.", - "OAuthException", - 2500)) + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + "An active access token must be used to query information about the current user.", + "OAuthException", + 2500)) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock(classOf[MockWSRequest]) - val wsResponse = mock(classOf[MockWSRequest#Response]) - when(wsResponse.status).thenReturn(500) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + override def running() = { + val wsRequest = mock(classOf[MockWSRequest]) + val wsResponse = mock(classOf[MockWSRequest#Response]) + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "return the social profile" in new WithApplication with Context { - val wsRequest = mock(classOf[MockWSRequest]) - val wsResponse = mock(classOf[MockWSRequest#Response]) - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/custom/facebook.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + override def running() = { + val wsRequest = mock(classOf[MockWSRequest]) + val wsResponse = mock(classOf[MockWSRequest#Response]) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/custom/facebook.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CustomSocialProfile( - loginInfo = LoginInfo(provider.id, "134405962728980"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - fullName = Some("Apollonia Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-ash2/t1/36245_155530314499277_2350717_n.jpg?lvh=1"), - gender = Some("male")) + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CustomSocialProfile( + loginInfo = LoginInfo(provider.id, "134405962728980"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + fullName = Some("Apollonia Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-ash2/t1/36245_155530314499277_2350717_n.jpg?lvh=1"), + gender = Some("male")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala index 3d6bb230..6f1a0a9a 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/LinkedInProviderSpec.scala @@ -36,80 +36,90 @@ class LinkedInProviderSpec extends OAuth1ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s => - s.copy("new-request-token-url") + override def running() = { + val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s => + s.copy("new-request-token-url") + } + val s: LinkedInProvider = provider.withSettings(overrideSettingsFunction) + + s.settings.requestTokenURL must be equalTo "new-request-token-url" + verify(oAuthService).withSettings(overrideSettingsFunction) } - val s: LinkedInProvider = provider.withSettings(overrideSettingsFunction) - - s.settings.requestTokenURL must be equalTo "new-request-token-url" - verify(oAuthService).withSettings(overrideSettingsFunction) } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/linkedin.error.json")) - when(httpLayer.url(API)).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - 0, - Some("Unknown authentication scheme"), - Some("LY860UAC5U"), - Some(401), - Some(1390421660154L))) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/linkedin.error.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + 0, + Some("Unknown authentication scheme"), + Some("LY860UAC5U"), + Some(401), + Some(1390421660154L))) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(httpLayer.url(API)).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(httpLayer.url(API)).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "use the overridden API URL" in new WithApplication with Context { - val url = "https://custom.api.url" - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(oAuthSettings.apiURL).thenReturn(Some(url)) - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/linkedin.success.json")) - when(httpLayer.url(url)).thenReturn(wsRequest) - - await(provider.retrieveProfile(oAuthInfo)) - - verify(httpLayer).url(url) + override def running() = { + val url = "https://custom.api.url" + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/linkedin.success.json")) + when(httpLayer.url(url)).thenReturn(wsRequest) + + await(provider.retrieveProfile(oAuthInfo)) + + verify(httpLayer).url(url) + } } "return the social profile" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/linkedin.success.json")) - when(httpLayer.url(API)).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo)) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "NhZXBl_O6f"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - fullName = Some("Apollonia Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("http://media.linkedin.com/mpr/mprx/0_fsPnURNRhLhk_Ue2fjKLUZkB2FL6TOe2S4bdUZz61GA9Ysxu_y_sz4THGW5JGJWhaMleN0F61-Dg")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/linkedin.success.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo)) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "NhZXBl_O6f"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + fullName = Some("Apollonia Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("http://media.linkedin.com/mpr/mprx/0_fsPnURNRhLhk_Ue2fjKLUZkB2FL6TOe2S4bdUZz61GA9Ysxu_y_sz4THGW5JGJWhaMleN0F61-Dg")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala index 76c1811c..7fef246b 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/TwitterProviderSpec.scala @@ -36,91 +36,103 @@ class TwitterProviderSpec extends OAuth1ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s => - s.copy("new-request-token-url") + override def running() = { + val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s => + s.copy("new-request-token-url") + } + val s: TwitterProvider = provider.withSettings(overrideSettingsFunction) + + s.settings.requestTokenURL must be equalTo "new-request-token-url" + verify(oAuthService).withSettings(overrideSettingsFunction) } - val s: TwitterProvider = provider.withSettings(overrideSettingsFunction) - - s.settings.requestTokenURL must be equalTo "new-request-token-url" - verify(oAuthService).withSettings(overrideSettingsFunction) } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.error.json")) - when(httpLayer.url(API)).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - 215, - Some("Bad Authentication data"))) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.error.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + 215, + Some("Bad Authentication data"))) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(httpLayer.url(API)).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(httpLayer.url(API)).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "use the overridden API URL" in new WithApplication with Context { - val url = "https://custom.api.url" - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(oAuthSettings.apiURL).thenReturn(Some(url)) - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.with.email.json")) - when(httpLayer.url(url)).thenReturn(wsRequest) - - await(provider.retrieveProfile(oAuthInfo)) - - verify(httpLayer).url(url) + override def running() = { + val url = "https://custom.api.url" + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.with.email.json")) + when(httpLayer.url(url)).thenReturn(wsRequest) + + await(provider.retrieveProfile(oAuthInfo)) + + verify(httpLayer).url(url) + } } "return the social profile" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.success.json")) - when(httpLayer.url(API)).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo)) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "6253282"), - fullName = Some("Apollonia Vanova"), - avatarURL = Some("https://pbs.twimg.com/profile_images/1209905677/appolonia_.jpg")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.success.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo)) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "6253282"), + fullName = Some("Apollonia Vanova"), + avatarURL = Some("https://pbs.twimg.com/profile_images/1209905677/appolonia_.jpg")) + } } } "return the social profile with email" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.with.email.json")) - when(httpLayer.url(API)).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo)) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "6253282"), - fullName = Some("Apollonia Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("https://pbs.twimg.com/profile_images/1209905677/appolonia_.jpg")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/twitter.with.email.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo)) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "6253282"), + fullName = Some("Apollonia Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("https://pbs.twimg.com/profile_images/1209905677/appolonia_.jpg")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala index 7e3dcad7..b82c6430 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/XingProviderSpec.scala @@ -36,77 +36,87 @@ class XingProviderSpec extends OAuth1ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s => - s.copy("new-request-token-url") + override def running() = { + val overrideSettingsFunction: OAuth1Settings => OAuth1Settings = { s => + s.copy("new-request-token-url") + } + val s: XingProvider = provider.withSettings(overrideSettingsFunction) + + s.settings.requestTokenURL must be equalTo "new-request-token-url" + verify(oAuthService).withSettings(overrideSettingsFunction) } - val s: XingProvider = provider.withSettings(overrideSettingsFunction) - - s.settings.requestTokenURL must be equalTo "new-request-token-url" - verify(oAuthService).withSettings(overrideSettingsFunction) } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/xing.error.json")) - when(httpLayer.url(API)).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - "INVALID_PARAMETERS", - "Invalid parameters (Limit must be a non-negative number.)")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/xing.error.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + "INVALID_PARAMETERS", + "Invalid parameters (Limit must be a non-negative number.)")) + } } } "throw ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(httpLayer.url(API)).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(httpLayer.url(API)).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo)) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "use the overridden API URL" in new WithApplication with Context { - val url = "https://custom.api.url" - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(oAuthSettings.apiURL).thenReturn(Some(url)) - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/xing.success.json")) - when(httpLayer.url(url)).thenReturn(wsRequest) - - await(provider.retrieveProfile(oAuthInfo)) - - verify(httpLayer).url(url) + override def running() = { + val url = "https://custom.api.url" + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/xing.success.json")) + when(httpLayer.url(url)).thenReturn(wsRequest) + + await(provider.retrieveProfile(oAuthInfo)) + + verify(httpLayer).url(url) + } } "return the social profile" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsRequest.sign(any)).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/xing.success.json")) - when(httpLayer.url(API)).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo)) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "1235468792"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - fullName = Some("Apollonia Vanova"), - avatarURL = Some("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.140x185.jpg"), - email = Some("apollonia.vanova@watchmen.com")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsRequest.sign(any)).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth1/xing.success.json")) + when(httpLayer.url(API)).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo)) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "1235468792"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + fullName = Some("Apollonia Vanova"), + avatarURL = Some("http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.140x185.jpg"), + email = Some("apollonia.vanova@watchmen.com")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala index 979819a1..b41e9788 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/secrets/CookieSecretSpec.scala @@ -55,62 +55,76 @@ class CookieSecretSpec extends PlaySpecification with JsonMatchers { "The `serialize` method of the secret" should { "sign the cookie" in new WithApplication with Context { - serialize(secret, signer, crypter) + override def running() = { + serialize(secret, signer, crypter) - verify(signer).sign(any()) + verify(signer).sign(any()) + } } "encrypt the cookie" in new WithApplication with Context { - serialize(secret, signer, crypter) + override def running() = { + serialize(secret, signer, crypter) - verify(crypter).encrypt(any()) + verify(crypter).encrypt(any()) + } } } "The `unserialize` method of the secret" should { "throw an OAuth1TokenSecretException if a secret contains invalid json" in new WithApplication with Context { - val value = "invalid" - val msg = Pattern.quote(InvalidJson.format(value)) + override def running() = { + val value = "invalid" + val msg = Pattern.quote(InvalidJson.format(value)) - unserialize(crypter.encrypt(value), signer, crypter) must beFailedTry.withThrowable[OAuth1TokenSecretException](msg) + unserialize(crypter.encrypt(value), signer, crypter) must beFailedTry.withThrowable[OAuth1TokenSecretException](msg) + } } "throw an OAuth1TokenSecretException if a secret contains valid json but invalid secret" in new WithApplication with Context { - val value = "{ \"test\": \"test\" }" - val msg = "^" + Pattern.quote(InvalidSecretFormat.format("")) + ".*" + override def running() = { + val value = "{ \"test\": \"test\" }" + val msg = "^" + Pattern.quote(InvalidSecretFormat.format("")) + ".*" - unserialize(crypter.encrypt(value), signer, crypter) must beFailedTry.withThrowable[OAuth1TokenSecretException](msg) + unserialize(crypter.encrypt(value), signer, crypter) must beFailedTry.withThrowable[OAuth1TokenSecretException](msg) + } } "throw an OAuth1TokenSecretException if a secret is badly signed" in new WithApplication with Context { - when(signer.extract(any())).thenReturn(Failure(new Exception("Bad signature"))) + override def running() = { + when(signer.extract(any())).thenReturn(Failure(new Exception("Bad signature"))) - val value = serialize(secret, signer, crypter) - val msg = Pattern.quote(InvalidCookieSignature) + val value = serialize(secret, signer, crypter) + val msg = Pattern.quote(InvalidCookieSignature) - unserialize(crypter.encrypt(value), signer, crypter) must beFailedTry.withThrowable[OAuth1TokenSecretException](msg) + unserialize(crypter.encrypt(value), signer, crypter) must beFailedTry.withThrowable[OAuth1TokenSecretException](msg) + } } } "The `serialize/unserialize` method of the secret" should { "serialize/unserialize a secret" in new WithApplication with Context { - val serialized = serialize(secret, signer, crypter) + override def running() = { + val serialized = serialize(secret, signer, crypter) - unserialize(serialized, signer, crypter) must beSuccessfulTry.withValue(secret) + unserialize(serialized, signer, crypter) must beSuccessfulTry.withValue(secret) + } } } "The `build` method of the provider" should { "return a new secret" in new WithApplication with Context { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - val dateTime = ZonedDateTime.of(2014, 8, 8, 0, 0, 0, 0, ZoneId.systemDefault) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() + val dateTime = ZonedDateTime.of(2014, 8, 8, 0, 0, 0, 0, ZoneId.systemDefault) - when(clock.now).thenReturn(dateTime) + when(clock.now).thenReturn(dateTime) - val s = await(provider.build(oAuthInfo)) + val s = await(provider.build(oAuthInfo)) - s.expirationDate must be equalTo dateTime.plusSeconds(settings.expirationTime.toSeconds.toInt) - s.value must be equalTo oAuthInfo.secret + s.expirationDate must be equalTo dateTime.plusSeconds(settings.expirationTime.toSeconds.toInt) + s.value must be equalTo oAuthInfo.secret + } } } @@ -124,60 +138,72 @@ class CookieSecretSpec extends PlaySpecification with JsonMatchers { } "throw an OAuth1TokenSecretException if secret is expired" in new WithApplication with Context { - val expiredSecret = secret.copy(expirationDate = ZonedDateTime.now.minusHours(1)) + override def running() = { + val expiredSecret = secret.copy(expirationDate = ZonedDateTime.now.minusHours(1)) - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(expiredSecret, signer, crypter))) + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(expiredSecret, signer, crypter))) - await(provider.retrieve) must throwA[OAuth1TokenSecretException].like { - case e => e.getMessage must startWith(SecretIsExpired.format()) + await(provider.retrieve) must throwA[OAuth1TokenSecretException].like { + case e => e.getMessage must startWith(SecretIsExpired.format()) + } } } "throw an OAuth1TokenSecretException if client secret contains invalid json" in new WithApplication with Context { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, crypter.encrypt("{"))) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, crypter.encrypt("{"))) - await(provider.retrieve) must throwA[OAuth1TokenSecretException].like { - case e => e.getMessage must startWith(InvalidJson.format("{")) + await(provider.retrieve) must throwA[OAuth1TokenSecretException].like { + case e => e.getMessage must startWith(InvalidJson.format("{")) + } } } "throw an OAuth1TokenSecretException if client secret contains valid json but invalid secret" in new WithApplication with Context { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, crypter.encrypt("{ \"test\": \"test\" }"))) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, crypter.encrypt("{ \"test\": \"test\" }"))) - await(provider.retrieve) must throwA[OAuth1TokenSecretException].like { - case e => e.getMessage must startWith(InvalidSecretFormat.format("")) + await(provider.retrieve) must throwA[OAuth1TokenSecretException].like { + case e => e.getMessage must startWith(InvalidSecretFormat.format("")) + } } } "throw an OAuth1TokenSecretException if client secret is badly signed" in new WithApplication with Context { - when(signer.extract(any())).thenReturn(Failure(new Exception("Bad signature"))) + override def running() = { + when(signer.extract(any())).thenReturn(Failure(new Exception("Bad signature"))) - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(secret, signer, crypter))) + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(secret, signer, crypter))) - await(provider.retrieve) must throwA[OAuth1TokenSecretException].like { - case e => e.getMessage must startWith(InvalidCookieSignature) + await(provider.retrieve) must throwA[OAuth1TokenSecretException].like { + case e => e.getMessage must startWith(InvalidCookieSignature) + } } } "return the secret if it's valid" in new WithApplication with Context { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(secret, signer, crypter))) + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest().withCookies(Cookie(settings.cookieName, CookieSecret.serialize(secret, signer, crypter))) - await(provider.retrieve) must be equalTo secret + await(provider.retrieve) must be equalTo secret + } } } "The `publish` method of the provider" should { "add the secret to the cookie" in new WithApplication with Context { - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/") - val result = Future.successful(provider.publish(Results.Ok, secret)) - - cookies(result).get(settings.cookieName) should beSome[Cookie].which { c => - c.name must be equalTo settings.cookieName - unserialize(c.value, signer, crypter).get must be equalTo secret - c.maxAge must beSome(settings.expirationTime.toSeconds.toInt) - c.path must be equalTo settings.cookiePath - c.domain must be equalTo settings.cookieDomain - c.secure must be equalTo settings.secureCookie + override def running() = { + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/") + val result = Future.successful(provider.publish(Results.Ok, secret)) + + cookies(result).get(settings.cookieName) should beSome[Cookie].which { c => + c.name must be equalTo settings.cookieName + unserialize(c.value, signer, crypter).get must be equalTo secret + c.maxAge must beSome(settings.expirationTime.toSeconds.toInt) + c.path must be equalTo settings.cookiePath + c.domain must be equalTo settings.cookieDomain + c.secure must be equalTo settings.secureCookie + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/services/PlayOAuth1ServiceSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/services/PlayOAuth1ServiceSpec.scala index 7ff4abe9..4d655d9a 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/services/PlayOAuth1ServiceSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth1/services/PlayOAuth1ServiceSpec.scala @@ -32,11 +32,13 @@ class PlayOAuth1ServiceSpec extends PlaySpecification { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = service.withSettings { s => - s.copy("new-request-token-url") - } + override def running() = { + val s = service.withSettings { s => + s.copy("new-request-token-url") + } - s.settings.requestTokenURL must be equalTo "new-request-token-url" + s.settings.requestTokenURL must be equalTo "new-request-token-url" + } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala index 0a15fbb5..7cfaefd7 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala @@ -38,133 +38,149 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s: Auth0Provider = provider.withSettings { s => - s.copy(accessTokenURL = "new-access-token-url") - } + override def running() = { + val s: Auth0Provider = provider.withSettings { s => + s.copy(accessTokenURL = "new-access-token-url") + } - s.settings.accessTokenURL must be equalTo "new-access-token-url" + s.settings.accessTokenURL must be equalTo "new-access-token-url" + } } } "The `authenticate` method" should { "fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(401) - when(wsResponse.body).thenReturn("Unauthorized") - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + } } } "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Json.obj()) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + } } } "return the auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + } } } "The `authenticate` method with user state" should { "return stateful auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) - when(state.items).thenReturn(Set(userStateItem)) - - statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) + + statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + } } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - - when(wsResponse.status).thenReturn(400) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}"))).thenReturn(wsRequest) - when(httpLayer.url(oAuthSettings.apiURL.get)).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfoObject)) { - case e => e.getMessage must equalTo(GenericHttpStatusProfileError.format(provider.id, 400)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + + when(wsResponse.status).thenReturn(400) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}"))).thenReturn(wsRequest) + when(httpLayer.url(oAuthSettings.apiURL.get)).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfoObject)) { + case e => e.getMessage must equalTo(GenericHttpStatusProfileError.format(provider.id, 400)) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - - when(wsResponse.status).thenReturn(500) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}"))).thenReturn(wsRequest) - when(httpLayer.url(oAuthSettings.apiURL.get)).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo("[Silhouette][auth0] error retrieving profile information") + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}"))).thenReturn(wsRequest) + when(httpLayer.url(oAuthSettings.apiURL.get)).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo("[Silhouette][auth0] error retrieving profile information") + } } } "return the social profile" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - val userProfile = Helper.loadJson(Auth0UserProfileJson) - - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(userProfile) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}"))).thenReturn(wsRequest) - when(httpLayer.url(oAuthSettings.apiURL.get)).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, (userProfile \ "sub").as[String]), - fullName = (userProfile \ "name").asOpt[String], - email = (userProfile \ "email").asOpt[String], - avatarURL = (userProfile \ "picture").asOpt[String]) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + val userProfile = Helper.loadJson(Auth0UserProfileJson) + + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(userProfile) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(wsRequest.withHttpHeaders(("Authorization", s"Bearer ${oAuthInfoObject.accessToken}"))).thenReturn(wsRequest) + when(httpLayer.url(oAuthSettings.apiURL.get)).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, (userProfile \ "sub").as[String]), + fullName = (userProfile \ "name").asOpt[String], + email = (userProfile \ "email").asOpt[String], + avatarURL = (userProfile \ "picture").asOpt[String]) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala index fbd79daa..d07a6500 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/DropboxProviderSpec.scala @@ -39,151 +39,169 @@ class DropboxProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s: DropboxProvider = provider.withSettings { s => - s.copy(accessTokenURL = "new-access-token-url") - } + override def running() = { + val s: DropboxProvider = provider.withSettings { s => + s.copy(accessTokenURL = "new-access-token-url") + } - s.settings.accessTokenURL must be equalTo "new-access-token-url" + s.settings.accessTokenURL must be equalTo "new-access-token-url" + } } } "The `authenticate` method" should { "fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(401) - when(wsResponse.body).thenReturn("Unauthorized") - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + } } } "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Json.obj()) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + } } } "return the auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + } } } "The `authenticate` method with user state" should { "return stateful auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) - when(state.items).thenReturn(Set(userStateItem)) - - statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) + + statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + } } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val authInfo = oAuthInfo.as[OAuth2Info] - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/dropbox.error.json")) - when(wsResponse.status).thenReturn(400) - when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - "Invalid OAuth request.", - 400)) + override def running() = { + val authInfo = oAuthInfo.as[OAuth2Info] + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/dropbox.error.json")) + when(wsResponse.status).thenReturn(400) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + "Invalid OAuth request.", + 400)) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val authInfo = oAuthInfo.as[OAuth2Info] - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(500) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + override def running() = { + val authInfo = oAuthInfo.as[OAuth2Info] + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "use the overridden API URL" in new WithApplication with Context { - val url = "https://custom.api.url" - val authInfo = oAuthInfo.as[OAuth2Info] - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(oAuthSettings.apiURL).thenReturn(Some(url)) - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/dropbox.success.json")) - when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(url)).thenReturn(wsRequest) - - await(provider.retrieveProfile(authInfo)) - - verify(httpLayer).url(url) + override def running() = { + val url = "https://custom.api.url" + val authInfo = oAuthInfo.as[OAuth2Info] + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/dropbox.success.json")) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url)).thenReturn(wsRequest) + + await(provider.retrieveProfile(authInfo)) + + verify(httpLayer).url(url) + } } "return the social profile" in new WithApplication with Context { - val authInfo = oAuthInfo.as[OAuth2Info] - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/dropbox.success.json")) - when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(authInfo)) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "12345678"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - fullName = Some("Apollonia Vanova")) + override def running() = { + val authInfo = oAuthInfo.as[OAuth2Info] + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/dropbox.success.json")) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(authInfo)) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "12345678"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + fullName = Some("Apollonia Vanova")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala index bd8eff30..54ecb8fb 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FacebookProviderSpec.scala @@ -39,146 +39,164 @@ class FacebookProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s: FacebookProvider = provider.withSettings { s => - s.copy(accessTokenURL = "new-access-token-url") - } + override def running() = { + val s: FacebookProvider = provider.withSettings { s => + s.copy(accessTokenURL = "new-access-token-url") + } - s.settings.accessTokenURL must be equalTo "new-access-token-url" + s.settings.accessTokenURL must be equalTo "new-access-token-url" + } } } "The `authenticate` method" should { "fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(401) - when(wsResponse.body).thenReturn("Unauthorized") - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + } } } "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Json.obj()) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + } } } "return the auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + } } } "The `authenticate` method with user state" should { "return stateful auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) - when(state.items).thenReturn(Set(userStateItem)) - - statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) + + statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + } } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(400) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/facebook.error.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - "An active access token must be used to query information about the current user.", - "OAuthException", - 2500)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(400) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/facebook.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + "An active access token must be used to query information about the current user.", + "OAuthException", + 2500)) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(500) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "use the overridden API URL" in new WithApplication with Context { - val url = "https://custom.api.url?access_token=%s" - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(oAuthSettings.apiURL).thenReturn(Some(url)) - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/facebook.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) - - await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - - verify(httpLayer).url(url.format("my.access.token")) + override def running() = { + val url = "https://custom.api.url?access_token=%s" + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/facebook.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) + + await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) + + verify(httpLayer).url(url.format("my.access.token")) + } } "return the social profile" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/facebook.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "134405962728980"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - fullName = Some("Apollonia Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-ash2/t1/36245_155530314499277_2350717_n.jpg?lvh=1")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/facebook.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "134405962728980"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + fullName = Some("Apollonia Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-ash2/t1/36245_155530314499277_2350717_n.jpg?lvh=1")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala index e2df277f..676b7212 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/FoursquareProviderSpec.scala @@ -39,205 +39,229 @@ class FoursquareProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s: FoursquareProvider = provider.withSettings { s => - s.copy(accessTokenURL = "new-access-token-url") - } + override def running() = { + val s: FoursquareProvider = provider.withSettings { s => + s.copy(accessTokenURL = "new-access-token-url") + } - s.settings.accessTokenURL must be equalTo "new-access-token-url" + s.settings.accessTokenURL must be equalTo "new-access-token-url" + } } } "The `authenticate` method" should { "fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(401) - when(wsResponse.body).thenReturn("Unauthorized") - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + } } } "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Json.obj()) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + } } } "return the auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + } } } "The `authenticate` method with user state" should { "return stateful auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) - when(state.items).thenReturn(Set(userStateItem)) - - statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) + + statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + } } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(400) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.error.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - 400, - Some("param_error"), - Some("Must provide a valid user ID or 'self.'"))) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(400) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + 400, + Some("param_error"), + Some("Must provide a valid user ID or 'self.'"))) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(500) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "use the overridden API URL" in new WithApplication with Context { - val url = "https://custom.api.url?access_token=%s" - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(oAuthSettings.apiURL).thenReturn(Some(url)) - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) - - await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - - verify(httpLayer).url(url.format("my.access.token")) + override def running() = { + val url = "https://custom.api.url?access_token=%s" + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) + + await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) + + verify(httpLayer).url(url.format("my.access.token")) + } } "return the social profile" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "13221052"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("https://irs0.4sqi.net/img/user/100x100/blank_girl.png")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "13221052"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("https://irs0.4sqi.net/img/user/100x100/blank_girl.png")) + } } } "return the social profile if API is deprecated" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.deprecated.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "13221052"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("https://irs0.4sqi.net/img/user/100x100/blank_girl.png")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.deprecated.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "13221052"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("https://irs0.4sqi.net/img/user/100x100/blank_girl.png")) + } } } "handle the custom API version property" in new WithApplication with Context { - val customProperties = Map(APIVersion -> "20120101") - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token", "20120101"))).thenReturn(wsRequest) - - profile(provider.withSettings(_.copy(customProperties = customProperties)) - .retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "13221052"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("https://irs0.4sqi.net/img/user/100x100/blank_girl.png")) + override def running() = { + val customProperties = Map(APIVersion -> "20120101") + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token", "20120101"))).thenReturn(wsRequest) + + profile(provider.withSettings(_.copy(customProperties = customProperties)) + .retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "13221052"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("https://irs0.4sqi.net/img/user/100x100/blank_girl.png")) + } } } "handle the custom avatar resolution property" in new WithApplication with Context { - val customProperties = Map(AvatarResolution -> "150x150") - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) - - profile(provider.withSettings(_.copy(customProperties = customProperties)) - .retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "13221052"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("https://irs0.4sqi.net/img/user/150x150/blank_girl.png")) + override def running() = { + val customProperties = Map(AvatarResolution -> "150x150") + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/foursquare.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token", DefaultAPIVersion))).thenReturn(wsRequest) + + profile(provider.withSettings(_.copy(customProperties = customProperties)) + .retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "13221052"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("https://irs0.4sqi.net/img/user/150x150/blank_girl.png")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala index e705ab9e..69487856 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitHubProviderSpec.scala @@ -40,151 +40,169 @@ class GitHubProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s: GitHubProvider = provider.withSettings { s => - s.copy(accessTokenURL = "new-access-token-url") - } + override def running() = { + val s: GitHubProvider = provider.withSettings { s => + s.copy(accessTokenURL = "new-access-token-url") + } - s.settings.accessTokenURL must be equalTo "new-access-token-url" + s.settings.accessTokenURL must be equalTo "new-access-token-url" + } } } "The `authenticate` method" should { "fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(401) - when(wsResponse.body).thenReturn("Unauthorized") - when(wsRequest.withHttpHeaders(HeaderNames.ACCEPT -> "application/json")).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(HeaderNames.ACCEPT -> "application/json")).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + } } } "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Json.obj()) - when(wsRequest.withHttpHeaders(HeaderNames.ACCEPT -> "application/json")).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(HeaderNames.ACCEPT -> "application/json")).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + } } } "return the auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + } } } "The `authenticate` method with user state" should { "return stateful auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) - when(state.items).thenReturn(Set(userStateItem)) - - statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) + + statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + } } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val authInfo = oAuthInfo.as[OAuth2Info] - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(400) - when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/github.error.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API)).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - "Bad credentials", - Some("http://developer.github.com/v3"))) + override def running() = { + val authInfo = oAuthInfo.as[OAuth2Info] + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(400) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/github.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API)).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + "Bad credentials", + Some("http://developer.github.com/v3"))) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val authInfo = oAuthInfo.as[OAuth2Info] - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(500) - when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API)).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + override def running() = { + val authInfo = oAuthInfo.as[OAuth2Info] + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(500) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API)).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(authInfo)) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "use the overridden API URL" in new WithApplication with Context { - val url = "https://custom.api.url" - val authInfo = oAuthInfo.as[OAuth2Info] - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(oAuthSettings.apiURL).thenReturn(Some(url)) - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/github.success.json")) - when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(url)).thenReturn(wsRequest) - - await(provider.retrieveProfile(authInfo)) - - verify(httpLayer).url(url) + override def running() = { + val url = "https://custom.api.url" + val authInfo = oAuthInfo.as[OAuth2Info] + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/github.success.json")) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url)).thenReturn(wsRequest) + + await(provider.retrieveProfile(authInfo)) + + verify(httpLayer).url(url) + } } "return the social profile" in new WithApplication with Context { - val authInfo = oAuthInfo.as[OAuth2Info] - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/github.success.json")) - when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API)).thenReturn(wsRequest) - - profile(provider.retrieveProfile(authInfo)) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "1"), - fullName = Some("Apollonia Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("https://github.com/images/error/apollonia_vanova.gif")) + override def running() = { + val authInfo = oAuthInfo.as[OAuth2Info] + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/github.success.json")) + when(wsRequest.withHttpHeaders(AUTHORIZATION -> s"Bearer ${authInfo.accessToken}")).thenReturn(wsRequest) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API)).thenReturn(wsRequest) + + profile(provider.retrieveProfile(authInfo)) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "1"), + fullName = Some("Apollonia Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("https://github.com/images/error/apollonia_vanova.gif")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala index 01ba36e6..5923939a 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GitLabProviderSpec.scala @@ -39,142 +39,160 @@ class GitLabProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s: GitLabProvider = provider.withSettings { s => - s.copy(accessTokenURL = "new-access-token-url") - } + override def running() = { + val s: GitLabProvider = provider.withSettings { s => + s.copy(accessTokenURL = "new-access-token-url") + } - s.settings.accessTokenURL must be equalTo "new-access-token-url" + s.settings.accessTokenURL must be equalTo "new-access-token-url" + } } } "The `authenticate` method" should { "fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(401) - when(wsResponse.body).thenReturn("Unauthorized") - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + } } } "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Json.obj()) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + } } } "return the auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + } } } "The `authenticate` method with user state" should { "return stateful auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) - when(state.items).thenReturn(Set(userStateItem)) - - statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) + + statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + } } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(400) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/gitlab.error.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - "Bad credentials")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(400) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/gitlab.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + "Bad credentials")) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(500) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "use the overridden API URL" in new WithApplication with Context { - val url = "https://custom.api.url?access_token=%s" - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(oAuthSettings.apiURL).thenReturn(Some(url)) - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/gitlab.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) - - await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - - verify(httpLayer).url(url.format("my.access.token")) + override def running() = { + val url = "https://custom.api.url?access_token=%s" + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/gitlab.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) + + await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) + + verify(httpLayer).url(url.format("my.access.token")) + } } "return the social profile" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/gitlab.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "1"), - fullName = Some("John Smith"), - email = Some("john@example.com"), - avatarURL = Some("http://gitlab.com/uploads/user/avatar/1/index.jpg")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/gitlab.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "1"), + fullName = Some("John Smith"), + email = Some("john@example.com"), + avatarURL = Some("http://gitlab.com/uploads/user/avatar/1/index.jpg")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala index 6bd827d3..973b0b95 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/GoogleProviderSpec.scala @@ -39,218 +39,244 @@ class GoogleProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s: GoogleProvider = provider.withSettings { s => - s.copy(accessTokenURL = "new-access-token-url") - } + override def running() = { + val s: GoogleProvider = provider.withSettings { s => + s.copy(accessTokenURL = "new-access-token-url") + } - s.settings.accessTokenURL must be equalTo "new-access-token-url" + s.settings.accessTokenURL must be equalTo "new-access-token-url" + } } } "The `authenticate` method" should { "fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(401) - when(wsResponse.body).thenReturn("Unauthorized") - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + } } } "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Json.obj()) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + } } } "return the auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + } } } "The `authenticate` method with user state" should { "return stateful auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) - when(state.items).thenReturn(Set(userStateItem)) - - statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) + + statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + } } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(401) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.error.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - 401, - "Invalid Credentials")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(401) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + 401, + "Invalid Credentials")) + } } } "fail with ProfileRetrievalException if API returns missing People API config" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(403) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.error.api.missing.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - val apiErrMsg = "People API has not been used in project 1234567890 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/people.googleapis.com/overview?project=1234567890 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry." - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - 403, - apiErrMsg)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(403) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.error.api.missing.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + val apiErrMsg = "People API has not been used in project 1234567890 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/people.googleapis.com/overview?project=1234567890 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry." + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + 403, + apiErrMsg)) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(500) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "use the overridden API URL" in new WithApplication with Context { - val url = "https://custom.api.url?access_token=%s" - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(oAuthSettings.apiURL).thenReturn(Some(url)) - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) - - await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - - verify(httpLayer).url(url.format("my.access.token")) + override def running() = { + val url = "https://custom.api.url?access_token=%s" + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) + + await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) + + verify(httpLayer).url(url.format("my.access.token")) + } } "return the social profile with an email" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "109476598527568979481"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - fullName = Some("Apollonia Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "109476598527568979481"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + fullName = Some("Apollonia Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50")) + } } } "return the social profile without an email" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.without.email.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "109476598527568979481"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - fullName = Some("Apollonia Vanova"), - email = None, - avatarURL = Some("https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.without.email.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "109476598527568979481"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + fullName = Some("Apollonia Vanova"), + email = None, + avatarURL = Some("https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50")) + } } } "return the social profile with an avatar url" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.img.non-default.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "109476598527568979481"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - fullName = Some("Apollonia Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.img.non-default.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "109476598527568979481"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + fullName = Some("Apollonia Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50")) + } } } "return the social profile without an avatar url" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.img.default.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "109476598527568979481"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - fullName = Some("Apollonia Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = None) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/google.img.default.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "109476598527568979481"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + fullName = Some("Apollonia Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = None) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala index 6e363ab6..468810b1 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/InstagramProviderSpec.scala @@ -39,143 +39,161 @@ class InstagramProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s: InstagramProvider = provider.withSettings { s => - s.copy(accessTokenURL = "new-access-token-url") - } + override def running() = { + val s: InstagramProvider = provider.withSettings { s => + s.copy(accessTokenURL = "new-access-token-url") + } - s.settings.accessTokenURL must be equalTo "new-access-token-url" + s.settings.accessTokenURL must be equalTo "new-access-token-url" + } } } "The `authenticate` method" should { "fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(401) - when(wsResponse.body).thenReturn("Unauthorized") - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + } } } "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Json.obj()) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + } } } "return the auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + } } } "The `authenticate` method with user state" should { "return stateful auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) - when(state.items).thenReturn(Set(userStateItem)) - - statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) + + statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + } } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(400) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/instagram.error.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - 400, - Some("OAuthAccessTokenException"), - Some("The access_token provided is invalid."))) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(400) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/instagram.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + 400, + Some("OAuthAccessTokenException"), + Some("The access_token provided is invalid."))) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(500) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "use the overridden API URL" in new WithApplication with Context { - val url = "https://custom.api.url?access_token=%s" - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(oAuthSettings.apiURL).thenReturn(Some(url)) - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/instagram.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) - - await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - - verify(httpLayer).url(url.format("my.access.token")) + override def running() = { + val url = "https://custom.api.url?access_token=%s" + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/instagram.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) + + await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) + + verify(httpLayer).url(url.format("my.access.token")) + } } "return the social profile" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/instagram.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "1574083"), - fullName = Some("Apollonia Vanova"), - avatarURL = Some("http://distillery.s3.amazonaws.com/profiles/profile_1574083_75sq_1295469061.jpg")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/instagram.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "1574083"), + fullName = Some("Apollonia Vanova"), + avatarURL = Some("http://distillery.s3.amazonaws.com/profiles/profile_1574083_75sq_1295469061.jpg")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala index b5919cc8..44d0b8c7 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/LinkedInProviderSpec.scala @@ -39,130 +39,146 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s: LinkedInProvider = provider.withSettings { s => - s.copy(accessTokenURL = "new-access-token-url") - } + override def running() = { + val s: LinkedInProvider = provider.withSettings { s => + s.copy(accessTokenURL = "new-access-token-url") + } - s.settings.accessTokenURL must be equalTo "new-access-token-url" + s.settings.accessTokenURL must be equalTo "new-access-token-url" + } } } "The `authenticate` method" should { "fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(401) - when(wsResponse.body).thenReturn("Unauthorized") - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + } } } "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Json.obj()) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + } } } "return the auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + } } } "The `authenticate` method with user state" should { "return stateful auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) - when(state.items).thenReturn(Set(userStateItem)) - - statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) + + statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + } } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(401) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.error.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - mockEmailAndPhoto(httpLayer) - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - 0, - Some("Unknown authentication scheme"), - Some("LY860UAC5U"), - Some(401), - Some(1390421660154L))) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(401) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + mockEmailAndPhoto(httpLayer) + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + 0, + Some("Unknown authentication scheme"), + Some("LY860UAC5U"), + Some(401), + Some(1390421660154L))) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(500) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - mockEmailAndPhoto(httpLayer) - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + mockEmailAndPhoto(httpLayer) + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "use the overridden API URL" in new WithApplication with Context { - val url = "https://custom.api.url?access_token=%s" - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(oAuthSettings.apiURL).thenReturn(Some(url)) - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) - mockEmailAndPhoto(httpLayer) - await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - - verify(httpLayer).url(url.format("my.access.token")) + override def running() = { + val url = "https://custom.api.url?access_token=%s" + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) + mockEmailAndPhoto(httpLayer) + await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) + + verify(httpLayer).url(url.format("my.access.token")) + } } def mockEmailAndPhoto(httpLayer: MockHTTPLayer) = { @@ -184,24 +200,26 @@ class LinkedInProviderSpec extends OAuth2ProviderSpec { } "return the social profile" in new WithApplication with Context { - // Basic profile - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - mockEmailAndPhoto(httpLayer) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "NhZXBl_O6f"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - fullName = Some("Apollonia Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("https://media.licdn.com/dms/image/C4E03AQFBprjocrF2iA/profile-displayphoto-shrink_100_100/0?e=1576108800&v=beta&t=Tn7mA43w8qmTuzjSdtuYQMi2kI5At9XOp8X--s5hpRU")) + override def running() = { + // Basic profile + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/linkedin.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + mockEmailAndPhoto(httpLayer) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "NhZXBl_O6f"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + fullName = Some("Apollonia Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("https://media.licdn.com/dms/image/C4E03AQFBprjocrF2iA/profile-displayphoto-shrink_100_100/0?e=1576108800&v=beta&t=Tn7mA43w8qmTuzjSdtuYQMi2kI5At9XOp8X--s5hpRU")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala index b2bbfe96..8ebd238b 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/VKProviderSpec.scala @@ -39,198 +39,222 @@ class VKProviderSpec extends OAuth2ProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s: VKProvider = provider.withSettings { s => - s.copy(accessTokenURL = "new-access-token-url") - } + override def running() = { + val s: VKProvider = provider.withSettings { s => + s.copy(accessTokenURL = "new-access-token-url") + } - s.settings.accessTokenURL must be equalTo "new-access-token-url" + s.settings.accessTokenURL must be equalTo "new-access-token-url" + } } } "The `authenticate` method" should { "fail with UnexpectedResponseException for an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(401) - when(wsResponse.body).thenReturn("Unauthorized") - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(401) + when(wsResponse.body).thenReturn("Unauthorized") + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(UnexpectedResponse.format(provider.id, "Unauthorized", 401)) + } } } "fail with UnexpectedResponseException if OAuth2Info can be build because of an unexpected response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Json.obj()) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - failed[UnexpectedResponseException](provider.authenticate()) { - case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Json.obj()) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + failed[UnexpectedResponseException](provider.authenticate()) { + case e => e.getMessage must startWith(InvalidInfoFormat.format(provider.id, "")) + } } } "return the auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - - authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + + authInfo(provider.authenticate())(_ must be equalTo oAuthInfo.as[OAuth2Info]) + } } } "The `authenticate` method with user state" should { "return stateful auth info" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(oAuthInfo) - when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) - when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) - when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) - when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) - when(state.items).thenReturn(Set(userStateItem)) - - statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + implicit val req: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "?" + Code + "=my.code") + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(oAuthInfo) + when(wsRequest.withHttpHeaders(any)).thenReturn(wsRequest) + when(wsRequest.post[Map[String, Seq[String]]](any)(any)).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(oAuthSettings.accessTokenURL)).thenReturn(wsRequest) + when(stateProvider.unserialize(anyString)(any[ExtractableRequest[String]], any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.state(any[ExecutionContext])).thenReturn(Future.successful(state)) + when(stateProvider.withHandler(any[SocialStateItemHandler])).thenReturn(stateProvider) + when(state.items).thenReturn(Set(userStateItem)) + + statefulAuthInfo(provider.authenticate(userStateItem))(_ must be equalTo stateAuthInfo) + } } } "The `retrieveProfile` method" should { "fail with ProfileRetrievalException if API returns error" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(400) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.error.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(SpecifiedProfileError.format( - provider.id, - 10, - "Internal server error: could not get application")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(400) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.error.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(SpecifiedProfileError.format( + provider.id, + 10, + "Internal server error: could not get application")) + } } } "fail with ProfileRetrievalException if an unexpected error occurred" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(500) - when(wsResponse.json).thenThrow(new RuntimeException("")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { - case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(500) + when(wsResponse.json).thenThrow(new RuntimeException("")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + failed[ProfileRetrievalException](provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { + case e => e.getMessage must equalTo(UnspecifiedProfileError.format(provider.id)) + } } } "use the overridden API URL" in new WithApplication with Context { - val url = "https://custom.api.url?access_token=%s" - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(oAuthSettings.apiURL).thenReturn(Some(url)) - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) - - await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) - - verify(httpLayer).url(url.format("my.access.token")) + override def running() = { + val url = "https://custom.api.url?access_token=%s" + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(oAuthSettings.apiURL).thenReturn(Some(url)) + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(url.format("my.access.token"))).thenReturn(wsRequest) + + await(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) + + verify(httpLayer).url(url.format("my.access.token")) + } } "return the social profile with email" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "66748"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("http://vk.com/images/camera_b.gif")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "66748"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("http://vk.com/images/camera_b.gif")) + } } } "return the social profile from response without photo" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.without.photo.json").as[JsObject]) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "66748"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = None) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.without.photo.json").as[JsObject]) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "66748"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = None) + } } } "return the social profile from deprecated API response" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.deprecated.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "66748"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("http://vk.com/images/camera_b.gif")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.deprecated.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + profile(provider.retrieveProfile(oAuthInfo.as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "66748"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("http://vk.com/images/camera_b.gif")) + } } } "return the social profile without email" in new WithApplication with Context { - val wsRequest = mock[MockWSRequest] - val wsResponse = mock[MockWSRequest#Response] - when(wsResponse.status).thenReturn(200) - when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.json")) - when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) - when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) - - profile(provider.retrieveProfile((oAuthInfo.as[JsObject] - "email").as[OAuth2Info])) { p => - p must be equalTo CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "66748"), - firstName = Some("Apollonia"), - lastName = Some("Vanova"), - email = None, - avatarURL = Some("http://vk.com/images/camera_b.gif")) + override def running() = { + val wsRequest = mock[MockWSRequest] + val wsResponse = mock[MockWSRequest#Response] + when(wsResponse.status).thenReturn(200) + when(wsResponse.json).thenReturn(Helper.loadJson("providers/oauth2/vk.success.json")) + when(wsRequest.get()).thenReturn(Future.successful(wsResponse)) + when(httpLayer.url(API.format("my.access.token"))).thenReturn(wsRequest) + + profile(provider.retrieveProfile((oAuthInfo.as[JsObject] - "email").as[OAuth2Info])) { p => + p must be equalTo CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "66748"), + firstName = Some("Apollonia"), + lastName = Some("Vanova"), + email = None, + avatarURL = Some("http://vk.com/images/camera_b.gif")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala index 7a327f44..cd44acaa 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/SteamProviderSpec.scala @@ -27,21 +27,25 @@ class SteamProviderSpec extends OpenIDProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val overrideSettingsFunction: OpenIDSettings => OpenIDSettings = { s => - s.copy("new-provider-url") - } - val s: SteamProvider = provider.withSettings(overrideSettingsFunction) + override def running() = { + val overrideSettingsFunction: OpenIDSettings => OpenIDSettings = { s => + s.copy("new-provider-url") + } + val s: SteamProvider = provider.withSettings(overrideSettingsFunction) - s.settings.providerURL must be equalTo "new-provider-url" - verify(openIDService).withSettings(overrideSettingsFunction) + s.settings.providerURL must be equalTo "new-provider-url" + verify(openIDService).withSettings(overrideSettingsFunction) + } } } "The `retrieveProfile` method" should { "return the social profile" in new WithApplication with Context { - profile(provider.retrieveProfile(openIDInfo)) { - case p => p must be equalTo new CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "http://steamcommunity.com/openid/id/16261495063738643")) + override def running() = { + profile(provider.retrieveProfile(openIDInfo)) { + case p => p must be equalTo new CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "http://steamcommunity.com/openid/id/16261495063738643")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala index 32995b78..213e5ba6 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/YahooProviderSpec.scala @@ -27,25 +27,29 @@ class YahooProviderSpec extends OpenIDProviderSpec { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val overrideSettingsFunction: OpenIDSettings => OpenIDSettings = { s => - s.copy("new-provider-url") - } - val s: YahooProvider = provider.withSettings(overrideSettingsFunction) + override def running() = { + val overrideSettingsFunction: OpenIDSettings => OpenIDSettings = { s => + s.copy("new-provider-url") + } + val s: YahooProvider = provider.withSettings(overrideSettingsFunction) - s.settings.providerURL must be equalTo "new-provider-url" - verify(openIDService).withSettings(overrideSettingsFunction) + s.settings.providerURL must be equalTo "new-provider-url" + verify(openIDService).withSettings(overrideSettingsFunction) + } } } "The `retrieveProfile` method" should { "return the social profile" in new WithApplication with Context { - profile(provider.retrieveProfile(openIDInfo)) { - case p => - p must be equalTo new CommonSocialProfile( - loginInfo = LoginInfo(provider.id, "https://me.yahoo.com/a/Xs6hPjazdrMvmbn4jhQjkjkhcasdGdsKajq9we"), - fullName = Some("Apollonia Vanova"), - email = Some("apollonia.vanova@watchmen.com"), - avatarURL = Some("https://s.yimg.com/dh/ap/social/profile/profile_b48.png")) + override def running() = { + profile(provider.retrieveProfile(openIDInfo)) { + case p => + p must be equalTo new CommonSocialProfile( + loginInfo = LoginInfo(provider.id, "https://me.yahoo.com/a/Xs6hPjazdrMvmbn4jhQjkjkhcasdGdsKajq9we"), + fullName = Some("Apollonia Vanova"), + email = Some("apollonia.vanova@watchmen.com"), + avatarURL = Some("https://s.yimg.com/dh/ap/social/profile/profile_b48.png")) + } } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala index 038916ed..f2e23be0 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala @@ -11,11 +11,13 @@ class PlayOpenIDServiceSpec extends PlaySpecification { "The `withSettings` method" should { "create a new instance with customized settings" in new WithApplication with Context { - val s = service.withSettings { s => - s.copy("new-provider-url") - } + override def running() = { + val s = service.withSettings { s => + s.copy("new-provider-url") + } - s.settings.providerURL must be equalTo "new-provider-url" + s.settings.providerURL must be equalTo "new-provider-url" + } } } From 4cab39bb1f4e6632d568753a9ff13dc84cca99fb Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Tue, 28 Nov 2023 16:41:16 +0100 Subject: [PATCH 21/30] Fix some providers tests --- .../impl/providers/GoogleTotpProviderSpec.scala | 4 ++-- .../impl/providers/oauth2/Auth0ProviderSpec.scala | 11 ++--------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala b/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala index 39235959..fc4ca1e8 100644 --- a/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala +++ b/silhouette-totp/src/test/scala/io/github/honeycombcheesecake/play/silhouette/impl/providers/GoogleTotpProviderSpec.scala @@ -52,7 +52,7 @@ class GoogleTotpProviderSpec extends PasswordProviderSpec { override def running() = { val googleAuthenticator = new GoogleAuthenticator() val validVerificationCode = googleAuthenticator.getTotpPassword(testSharedKey) - await(provider.authenticate(testSharedKey, validVerificationCode.toString)) should not be empty + await(provider.authenticate(testSharedKey, validVerificationCode.toString)) should not be None } } } @@ -94,7 +94,7 @@ class GoogleTotpProviderSpec extends PasswordProviderSpec { when(fooHasher.hash(any())).thenReturn(testPasswordInfo) when(barHasher.matches(testPasswordInfo, testScratchCode)).thenReturn(true) val result = provider.createCredentials(credentials.identifier) - await(provider.authenticate(result.totpInfo, testScratchCode)) should not be empty + await(provider.authenticate(result.totpInfo, testScratchCode)) should not be None } } } diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala index 7cfaefd7..f3b2ceab 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/oauth2/Auth0ProviderSpec.scala @@ -166,7 +166,7 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { override def running() = { val wsRequest = mock[MockWSRequest] val wsResponse = mock[MockWSRequest#Response] - val userProfile = Helper.loadJson(Auth0UserProfileJson) + val userProfile = Helper.loadJson("providers/custom/auth0.profile.json") when(wsResponse.status).thenReturn(200) when(wsResponse.json).thenReturn(userProfile) @@ -197,13 +197,6 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { */ trait Context extends OAuth2ProviderSpecContext { - /** - * Paths to the Json fixtures. - */ - val Auth0ErrorJson = "providers/custom/auth0.error.json" - val Auth0SuccessJson = "providers/custom/auth0.success.json" - val Auth0UserProfileJson = "providers/custom/auth0.profile.json" - /** * The OAuth2 settings. */ @@ -219,7 +212,7 @@ class Auth0ProviderSpec extends OAuth2ProviderSpec { /** * The OAuth2 info returned by Auth0. */ - override lazy val oAuthInfo = Helper.loadJson(Auth0SuccessJson) + override lazy val oAuthInfo = Helper.loadJson("providers/custom/auth0.success.json") /** * The OAuth2 info deserialized as case class object From ddae0638596eba0bc0c9b8f949f1bd9341576e64 Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Tue, 28 Nov 2023 17:54:59 +0100 Subject: [PATCH 22/30] Fix tests for scala 3 --- .../impl/authenticators/BearerTokenAuthenticatorSpec.scala | 2 +- .../impl/providers/openid/service/PlayOpenIDServiceSpec.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala index 421b5578..98caa376 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala @@ -331,7 +331,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification { "remove authenticator from backing store" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - when(repository.remove(authenticator.id)).thenReturn(Future.successful(authenticator)) + when(repository.remove(authenticator.id)).thenReturn(Future.unit) await(service.discard(authenticator, Results.Ok)) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala index f2e23be0..0993854c 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala @@ -37,7 +37,7 @@ class PlayOpenIDServiceSpec extends PlaySpecification { "image" -> "http://axschema.org/media/image/default"), realm = Some("http://localhost:9000")) - val service = new PlayOpenIDService(mock(classOf[OpenIdClient]), openIDSettings) + lazy val service = new PlayOpenIDService(mock(classOf[OpenIdClient]), openIDSettings) } } From 4ab8fc9153fef218f137047fb2df2756eaab795f Mon Sep 17 00:00:00 2001 From: mathis guillet Date: Wed, 29 Nov 2023 09:42:37 +0100 Subject: [PATCH 23/30] Fix tests JWTAuthenticatorSpecs --- .../silhouette/impl/authenticators/JWTAuthenticatorSpec.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala index d3bbc18a..b0b1d68e 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala @@ -611,7 +611,7 @@ class JWTAuthenticatorSpec extends PlaySpecification with JsonMatchers { "remove authenticator from backing store if DAO is enabled" in new Context { implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() - when(repository.remove(authenticator.id)).thenReturn(Future.successful(authenticator)) + when(repository.remove(authenticator.id)).thenReturn(Future.unit) await(service(Some(repository)).discard(authenticator, Results.Ok)) @@ -644,7 +644,7 @@ class JWTAuthenticatorSpec extends PlaySpecification with JsonMatchers { */ trait Context extends Scope { - private val lastUsedDateTime = ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0) + private lazy val lastUsedDateTime = ZonedDateTime.now.`with`(ChronoField.MILLI_OF_SECOND, 0) /** * The repository implementation. From 9fcb3e30d9ffe1e0923bf5cfd569ad2872199078 Mon Sep 17 00:00:00 2001 From: MathisGuillet1 Date: Wed, 29 Nov 2023 10:34:33 +0100 Subject: [PATCH 24/30] Enable cross compile --- build.sbt | 13 +++++++++---- project/Dependencies.scala | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/build.sbt b/build.sbt index aa6a3994..42abf865 100644 --- a/build.sbt +++ b/build.sbt @@ -3,8 +3,8 @@ import sbt.CrossVersion lazy val repo: String = "https://s01.oss.sonatype.org" lazy val scala213: String = "2.13.12" -lazy val scala3: String = "3.3.1" // Ready for cross build, currently not yet supported by play. -lazy val supportedScalaVersions: Seq[String] = Seq(/*scala213,*/ scala3) +lazy val scala3: String = "3.3.1" +lazy val supportedScalaVersions: Seq[String] = Seq(scala213, scala3) Global / evictionErrorLevel := Level.Info @@ -16,7 +16,7 @@ ThisBuild / Test / publishArtifact := false ThisBuild / pomIncludeRepository := { _ => false } ThisBuild / organization := "io.github.honeycomb-cheesecake" ThisBuild / organizationName := "honeycomb-cheesecake" -ThisBuild / scalaVersion := scala3 +ThisBuild / scalaVersion := scala213 ThisBuild / versionScheme := Some("early-semver") ThisBuild / scalacOptions ++= Seq( //"-unchecked", @@ -227,13 +227,18 @@ lazy val silhouetteTestkit = (project in file("silhouette-testkit")) libraryDependencies ++= Library.updates ++ Seq( Library.Play.test, - Library.izumiReflect, Library.Play.specs2 % Test, Library.Specs2.matcherExtra % Test, Library.mockito % Test, Library.scalaGuice % Test, Library.akkaTestkit % Test ) + ++ { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((3, _)) => Seq(Library.izumiReflect) + case _ => Seq.empty + } + } ) .enablePlugins(PlayScala) .dependsOn(silhouette) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 3a898056..cb727e68 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -35,7 +35,7 @@ object Dependencies { } object Specs2 { - private val version = "4.20.2" // Versions later than this will fail due to removed dependencies. + private val version = "4.20.2" val core = "org.specs2" %% "specs2-core" % version val matcherExtra = "org.specs2" %% "specs2-matcher-extra" % version } @@ -51,6 +51,6 @@ object Dependencies { val casClientSupportSAML = "org.jasig.cas.client" % "cas-client-support-saml" % "3.6.4" val apacheCommonLang = "org.apache.commons" % "commons-lang3" % "3.13.0" val googleAuth = "com.warrenstrange" % "googleauth" % "1.5.0" - val izumiReflect = "dev.zio" %% "izumi-reflect" % "2.3.8" + val izumiReflect = "dev.zio" %% "izumi-reflect" % "2.3.8" // Scala 3 replacement for scala 2 reflect universe } } From 76234e4823cef31167e03e3c4c27767dcb0d8cd9 Mon Sep 17 00:00:00 2001 From: MathisGuillet1 Date: Wed, 29 Nov 2023 11:17:40 +0100 Subject: [PATCH 25/30] Fully working cross compiled version --- .../play/silhouette/test/Fakes.scala | 245 +++++++++++ .../play/silhouette/test/package.scala | 76 ++++ .../play/silhouette/test/Fakes.scala | 0 .../play/silhouette/test/package.scala | 0 .../play/silhouette/api/Authenticator.scala | 164 +++++++ .../play/silhouette/api/Environment.scala | 109 +++++ .../play/silhouette/api/RequestHandler.scala | 227 ++++++++++ .../api/actions/SecuredAction.scala | 408 ++++++++++++++++++ .../api/actions/UserAwareAction.scala | 259 +++++++++++ .../api/services/AuthenticatorService.scala | 226 ++++++++++ .../play/silhouette/api/Authenticator.scala | 0 .../play/silhouette/api/Environment.scala | 0 .../play/silhouette/api/RequestHandler.scala | 0 .../api/actions/SecuredAction.scala | 0 .../api/actions/UserAwareAction.scala | 0 .../api/services/AuthenticatorService.scala | 0 .../custom/FacebookProviderSpec.scala | 2 +- 17 files changed, 1715 insertions(+), 1 deletion(-) create mode 100644 silhouette-testkit/app-2/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala create mode 100644 silhouette-testkit/app-2/io/github/honeycombcheesecake/play/silhouette/test/package.scala rename silhouette-testkit/{app => app-3}/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala (100%) rename silhouette-testkit/{app => app-3}/io/github/honeycombcheesecake/play/silhouette/test/package.scala (100%) create mode 100644 silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala create mode 100644 silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala create mode 100644 silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala create mode 100644 silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala create mode 100644 silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala create mode 100644 silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala rename silhouette/{app => app-3}/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala (100%) rename silhouette/{app => app-3}/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala (100%) rename silhouette/{app => app-3}/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala (100%) rename silhouette/{app => app-3}/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala (100%) rename silhouette/{app => app-3}/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala (100%) rename silhouette/{app => app-3}/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala (100%) diff --git a/silhouette-testkit/app-2/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala b/silhouette-testkit/app-2/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala new file mode 100644 index 00000000..ca49eb75 --- /dev/null +++ b/silhouette-testkit/app-2/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala @@ -0,0 +1,245 @@ +/** + * Copyright 2015 Mohiva Organisation (license at mohiva dot com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.github.honeycombcheesecake.play.silhouette.test + +import java.util.UUID +import io.github.honeycombcheesecake.play.silhouette.api._ +import io.github.honeycombcheesecake.play.silhouette.api.crypto.{ Base64AuthenticatorEncoder, Signer } +import io.github.honeycombcheesecake.play.silhouette.api.repositories.AuthenticatorRepository +import io.github.honeycombcheesecake.play.silhouette.api.services.{ AuthenticatorService, IdentityService } +import io.github.honeycombcheesecake.play.silhouette.api.util.Clock +import io.github.honeycombcheesecake.play.silhouette.impl.authenticators._ +import io.github.honeycombcheesecake.play.silhouette.impl.util.{ DefaultFingerprintGenerator, SecureRandomIDGenerator } +import play.api.mvc.{ DefaultCookieHeaderEncoding, DefaultSessionCookieBaker, RequestHeader } + +import scala.collection.mutable +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.{ ExecutionContext, Future } +import scala.reflect.runtime.universe._ +import scala.util +import scala.util.Success + +/** + * A fake identity. + * + * @param loginInfo The linked login info for an identity. + */ +final case class FakeIdentity(loginInfo: LoginInfo) extends Identity + +/** + * A fake identity service implementation which can handle a predefined list of identities. + * + * @param identities A list of (login info -> identity) pairs this service is responsible for. + * @tparam I The type of the identity to handle. + */ +class FakeIdentityService[I <: Identity](identities: (LoginInfo, I)*) extends IdentityService[I] { + + /** + * Retrieves an identity that matches the specified login info. + * + * @param loginInfo The login info to retrieve an identity. + * @return The retrieved identity or None if no identity could be retrieved for the given login info. + */ + def retrieve(loginInfo: LoginInfo): Future[Option[I]] = { + Future.successful(identities.find(_._1 == loginInfo).map(_._2)) + } +} + +/** + * A fake authenticator repository which persists authenticators in memory. + * + * @tparam T The type of the authenticator to handle. + */ +class FakeAuthenticatorRepository[T <: StorableAuthenticator] extends AuthenticatorRepository[T] { + + /** + * The data store for the OAuth1 info. + */ + var data: mutable.HashMap[String, T] = mutable.HashMap() + + /** + * Finds the authenticator for the given ID. + * + * @param id The authenticator ID. + * @return The found authenticator or None if no authenticator could be found for the given ID. + */ + def find(id: String): Future[Option[T]] = { + Future.successful(data.get(id)) + } + + /** + * Adds a new authenticator. + * + * @param authenticator The authenticator to add. + * @return The added authenticator. + */ + def add(authenticator: T): Future[T] = { + data += (authenticator.id -> authenticator) + Future.successful(authenticator) + } + + /** + * Updates an already existing authenticator. + * + * @param authenticator The authenticator to update. + * @return The updated authenticator. + */ + def update(authenticator: T): Future[T] = { + data += (authenticator.id -> authenticator) + Future.successful(authenticator) + } + + /** + * Removes the authenticator for the given ID. + * + * @param id The authenticator ID. + * @return An empty future. + */ + def remove(id: String): Future[Unit] = { + data -= id + Future.successful(()) + } +} + +/** + * A fake session authenticator service. + */ +case object FakeSessionAuthenticatorService extends SessionAuthenticatorService( + SessionAuthenticatorSettings(), + new DefaultFingerprintGenerator(), + new Base64AuthenticatorEncoder, + new DefaultSessionCookieBaker(), + Clock()) + +/** + * A fake cookie authenticator service. + */ +case object FakeCookieAuthenticatorService extends CookieAuthenticatorService( + CookieAuthenticatorSettings(), + None, + new Signer { + def sign(data: String): String = data + def extract(message: String): util.Try[String] = Success(message) + }, + new DefaultCookieHeaderEncoding(), + new Base64AuthenticatorEncoder, + new DefaultFingerprintGenerator(), + new SecureRandomIDGenerator(), + Clock()) + +/** + * A fake bearer token authenticator service. + */ +case object FakeBearerTokenAuthenticatorService extends BearerTokenAuthenticatorService( + BearerTokenAuthenticatorSettings(), + new FakeAuthenticatorRepository[BearerTokenAuthenticator], + new SecureRandomIDGenerator(), + Clock()) + +/** + * A fake JWT authenticator service. + */ +case object FakeJWTAuthenticatorService extends JWTAuthenticatorService( + JWTAuthenticatorSettings(sharedSecret = UUID.randomUUID().toString), + None, + new Base64AuthenticatorEncoder, + new SecureRandomIDGenerator(), + Clock()) + +/** + * A fake Dummy authenticator service. + */ +case object FakeDummyAuthenticatorService extends DummyAuthenticatorService + +/** + * A fake authenticator service factory. + */ +object FakeAuthenticatorService { + + /** + * Creates a new fake authenticator for the given authenticator type. + * + * @tparam T The type of the authenticator. + * @return A fully configured authenticator instance. + */ + def apply[T <: Authenticator: TypeTag](): AuthenticatorService[T] = { + (typeOf[T] match { + case t if t <:< typeOf[SessionAuthenticator] => FakeSessionAuthenticatorService + case t if t <:< typeOf[CookieAuthenticator] => FakeCookieAuthenticatorService + case t if t <:< typeOf[BearerTokenAuthenticator] => FakeBearerTokenAuthenticatorService + case t if t <:< typeOf[JWTAuthenticator] => FakeJWTAuthenticatorService + case t if t <:< typeOf[DummyAuthenticator] => FakeDummyAuthenticatorService + case _ => throw new Exception("Invalid type specified.") + }).asInstanceOf[AuthenticatorService[T]] + } +} + +/** + * A fake authenticator. + * + * @param loginInfo The linked login info for an identity. + * @param id The ID of the authenticator. + * @param isValid True if the authenticator is valid, false otherwise. + */ +final case class FakeAuthenticator(loginInfo: LoginInfo, id: String = UUID.randomUUID().toString, isValid: Boolean = true) extends StorableAuthenticator + +/** + * A fake authenticator factory. + */ +object FakeAuthenticator { + + /** + * Creates a new fake authenticator for the given authenticator type. + * + * @param loginInfo The login info for which the authenticator should be created. + * @param env The Silhouette environment. + * @param requestHeader The request header. + * @tparam E The type of the environment, + * @return A authenticator instance. + */ + def apply[E <: Env](loginInfo: LoginInfo)(implicit env: Environment[E], requestHeader: RequestHeader): E#A = { + env.authenticatorService.create(loginInfo) + } +} + +/** + * A fake environment implementation. + * + * @param identities A list of (login info -> identity) pairs to return inside a Silhouette action. + * @param requestProviders The list of request providers. + * @param eventBus The event bus implementation. + * @param executionContext The execution context to handle the asynchronous operations. + * @param tt The type tag of the authenticator type. + * @tparam E The type of the environment. + */ +final case class FakeEnvironment[E <: Env]( + identities: Seq[(LoginInfo, E#I)], + requestProviders: Seq[RequestProvider] = Seq.empty, + eventBus: EventBus = EventBus())( + implicit + val executionContext: ExecutionContext, + tt: TypeTag[E#A]) extends Environment[E] { + + /** + * The identity service implementation. + */ + val identityService: IdentityService[E#I] = new FakeIdentityService[E#I](identities: _*) + + /** + * The authenticator service implementation. + */ + val authenticatorService: AuthenticatorService[E#A] = FakeAuthenticatorService[E#A]() +} diff --git a/silhouette-testkit/app-2/io/github/honeycombcheesecake/play/silhouette/test/package.scala b/silhouette-testkit/app-2/io/github/honeycombcheesecake/play/silhouette/test/package.scala new file mode 100644 index 00000000..c3ea0424 --- /dev/null +++ b/silhouette-testkit/app-2/io/github/honeycombcheesecake/play/silhouette/test/package.scala @@ -0,0 +1,76 @@ +/** + * Copyright 2015 Mohiva Organisation (license at mohiva dot com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.github.honeycombcheesecake.play.silhouette + +import io.github.honeycombcheesecake.play.silhouette.api._ +import play.api.mvc.Request +import play.api.test.FakeRequest + +import scala.concurrent.duration._ +import scala.concurrent.{ Await, Future } +import scala.language.{ implicitConversions, postfixOps } + +/** + * Test helpers to test a Silhouette application. + */ +package object test { + + /** + * Resolves a future by waiting of the result. + * + * @param f The future to block. + * @tparam T The type contained in the future. + * @return The value contained in the future. + */ + implicit protected[test] def await[T](f: Future[T]): T = Await.result(f, 60 seconds) + + /** + * Provides a method which add an authenticator to a fake request. + * + * @param f A fake request instance. + * @tparam A The type of the body. + */ + implicit class FakeRequestWithAuthenticator[A](f: FakeRequest[A]) { + implicit val request: FakeRequest[A] = f + + /** + * Creates a fake request with an embedded authenticator. + * + * @param authenticator The authenticator to embed into the request. + * @param env The Silhouette environment. + * @tparam E The type of the environment. + * @return A fake request. + */ + def withAuthenticator[E <: Env](authenticator: E#A)(implicit env: Environment[E]): FakeRequest[A] = { + implicit val ec = env.executionContext + val rh = env.authenticatorService.init(authenticator).map(v => env.authenticatorService.embed(v, f)) + + new FakeRequest(Request.apply(rh, f.body)) + } + + /** + * Creates a fake request with an embedded authenticator. + * + * @param loginInfo The login info for which the new authenticator should be created. + * @param env The Silhouette environment. + * @tparam E The type of the environment. + * @return A fake request. + */ + def withAuthenticator[E <: Env](loginInfo: LoginInfo)(implicit env: Environment[E]): FakeRequest[A] = { + withAuthenticator(FakeAuthenticator[E](loginInfo)) + } + } +} diff --git a/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala b/silhouette-testkit/app-3/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala similarity index 100% rename from silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala rename to silhouette-testkit/app-3/io/github/honeycombcheesecake/play/silhouette/test/Fakes.scala diff --git a/silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/package.scala b/silhouette-testkit/app-3/io/github/honeycombcheesecake/play/silhouette/test/package.scala similarity index 100% rename from silhouette-testkit/app/io/github/honeycombcheesecake/play/silhouette/test/package.scala rename to silhouette-testkit/app-3/io/github/honeycombcheesecake/play/silhouette/test/package.scala diff --git a/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala new file mode 100644 index 00000000..1d16b870 --- /dev/null +++ b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala @@ -0,0 +1,164 @@ +/** + * Original work: SecureSocial (https://github.com/jaliss/securesocial) + * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss + * + * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) + * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.github.honeycombcheesecake.play.silhouette.api + +import io.github.honeycombcheesecake.play.silhouette.api.Authenticator.Implicits._ + +import java.time.ZonedDateTime +import scala.concurrent.duration.FiniteDuration + +/** + * An authenticator recognizes an authenticated user. + */ +trait Authenticator { + + /** + * The Type of the generated value an authenticator will be serialized to. + */ + type Value + + /** + * The type of the settings an authenticator can handle. + */ + type Settings + + /** + * Gets the linked login info for an identity. + * + * @return The linked login info for an identity. + */ + def loginInfo: LoginInfo + + /** + * Checks if the authenticator valid. + * + * @return True if the authenticator valid, false otherwise. + */ + def isValid: Boolean +} + +/** + * The `Authenticator` companion object. + */ +object Authenticator { + + /** + * Some implicits. + */ + object Implicits { + + /** + * Defines additional methods on a `ZonedDateTime` instance. + * + * @param dateTime The `ZonedDateTime` instance on which the additional methods should be defined. + */ + implicit class RichDateTime(dateTime: ZonedDateTime) { + + /** + * Adds a duration to a date/time. + * + * @param duration The duration to add. + * @return A date/time instance with the added duration. + */ + def +(duration: FiniteDuration): ZonedDateTime = { + dateTime.plusSeconds(duration.toSeconds.toInt) + } + + /** + * Subtracts a duration from a date/time. + * + * @param duration The duration to subtract. + * @return A date/time instance with the subtracted duration. + */ + def -(duration: FiniteDuration): ZonedDateTime = { + dateTime.minusSeconds(duration.toSeconds.toInt) + } + + /** + * Compares a date/time with the current time + * + * @return Is the current time before the time supplied by the Clock + */ + def isBeforeNow: Boolean = { + dateTime.isBefore(ZonedDateTime.now) + } + } + } +} + +/** + * An authenticator which can be stored in a backing store. + */ +trait StorableAuthenticator extends Authenticator { + + /** + * Gets the ID to reference the authenticator in the backing store. + * + * @return The ID to reference the authenticator in the backing store. + */ + def id: String +} + +/** + * An authenticator that may expire. + */ +trait ExpirableAuthenticator extends Authenticator { + + /** + * The last used date/time. + */ + val lastUsedDateTime: ZonedDateTime + + /** + * The expiration date/time. + */ + val expirationDateTime: ZonedDateTime + + /** + * The duration an authenticator can be idle before it timed out. + */ + val idleTimeout: Option[FiniteDuration] + + /** + * Checks if the authenticator isn't expired and isn't timed out. + * + * @return True if the authenticator isn't expired and isn't timed out. + */ + override def isValid: Boolean = !isExpired && !isTimedOut + + /** + * Checks if the authenticator is expired. This is an absolute timeout since the creation of + * the authenticator. + * + * @return True if the authenticator is expired, false otherwise. + */ + def isExpired: Boolean = expirationDateTime.isBeforeNow + + /** + * Checks if the time elapsed since the last time the authenticator was used, is longer than + * the maximum idle timeout specified in the properties. + * + * @return True if sliding window expiration is activated and the authenticator is timed out, false otherwise. + */ + def isTimedOut: Boolean = idleTimeout match { + case Some(idle) => (lastUsedDateTime + idle).isBeforeNow + case _ => false + } +} diff --git a/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala new file mode 100644 index 00000000..0cd8e6e2 --- /dev/null +++ b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala @@ -0,0 +1,109 @@ +/** + * Copyright 2015 Mohiva Organisation (license at mohiva dot com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.github.honeycombcheesecake.play.silhouette.api + +import io.github.honeycombcheesecake.play.silhouette.api.services.{ AuthenticatorService, IdentityService } +import io.github.honeycombcheesecake.play.silhouette.api.util.ExecutionContextProvider + +import scala.concurrent.ExecutionContext + +/** + * The environment type. + * + * Defines the [[Identity]] and [[Authenticator]] types for an environment. It is possible + * to implement as many types as needed. This has the advantage that an application isn't + * bound only to a single `Identity` -> `Authenticator` combination. + * + * To define a new environment type create a new trait with the appropriate [[Identity]] and + * [[Authenticator]] types: + * + * {{{ + * trait SessionEnv extends Env { + * type I = User + * type A = SessionAuthenticator + * } + * trait JWTEnv extends Env { + * type I = User + * type A = JWTAuthenticator + * } + * }}} + */ +trait Env { + type I <: Identity + type A <: Authenticator +} + +/** + * Provides the components needed to handle a secured request. + * + * It's possible to declare different environments for different environment types. The + * [[io.github.honeycombcheesecake.play.silhouette.api.services.IdentityService]] and the + * [[io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorService]] are bound to the appropriate types + * defined in the environment type. But the [[EventBus]] and the list of [[RequestProvider]] + * instances can be defined as needed for every environment type. + */ +trait Environment[E <: Env] extends ExecutionContextProvider { + + /** + * Gets the identity service implementation. + * + * @return The identity service implementation. + */ + def identityService: IdentityService[E#I] + + /** + * Gets the authenticator service implementation. + * + * @return The authenticator service implementation. + */ + def authenticatorService: AuthenticatorService[E#A] + + /** + * Gets the list of request providers. + * + * @return The list of request providers. + */ + def requestProviders: Seq[RequestProvider] + + /** + * The event bus implementation. + * + * @return The event bus implementation. + */ + def eventBus: EventBus +} + +/** + * Companion object to easily create environment instances. + * + * {{{ + * Environment[SessionEnv](...) + * Environment[JWTEnv](...) + * }}} + */ +object Environment { + def apply[E <: Env]( + identityServiceImpl: IdentityService[E#I], + authenticatorServiceImpl: AuthenticatorService[E#A], + requestProvidersImpl: Seq[RequestProvider], + eventBusImpl: EventBus)(implicit ec: ExecutionContext) = new Environment[E] { + val identityService = identityServiceImpl + val authenticatorService = authenticatorServiceImpl + val requestProviders = requestProvidersImpl + val eventBus = eventBusImpl + val executionContext = ec + } +} diff --git a/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala new file mode 100644 index 00000000..049d20ab --- /dev/null +++ b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala @@ -0,0 +1,227 @@ +/** + * Original work: SecureSocial (https://github.com/jaliss/securesocial) + * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss + * + * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) + * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.github.honeycombcheesecake.play.silhouette.api + +import io.github.honeycombcheesecake.play.silhouette.api.services.AuthenticatorResult +import io.github.honeycombcheesecake.play.silhouette.api.util.ExecutionContextProvider +import play.api.mvc.{ Result, RequestHeader, Request, AnyContent } + +import scala.concurrent.ExecutionContext +import scala.concurrent.Future + +/** + * A result which can transport a result as also additional data through the request handler process. + * + * @param result A Play Framework result. + * @param data Additional data to transport in the result. + * @tparam T The type of the data. + */ +final case class HandlerResult[+T](result: Result, data: Option[T] = None) + +/** + * Base implementation for building request handlers. + * + * The base implementations to handle secured endpoints are encapsulated into request handlers which + * can execute an arbitrary block of code and must return a HandlerResult. This HandlerResult consists + * of a normal Play result and arbitrary additional data which can be transported out of these handlers. + * + * @tparam E The type of the environment. + * @tparam R The type of the request. + */ +trait RequestHandlerBuilder[E <: Env, +R[_]] extends ExecutionContextProvider { + + /** + * Provides an `extract` method on an `Either` which contains the same types. + */ + protected implicit class ExtractEither[T](r: Either[T, T]) { + def extract: T = r.fold(identity, identity) + } + + /** + * The execution context to handle the asynchronous operations. + */ + implicit lazy val executionContext: ExecutionContext = environment.executionContext + + /** + * The environment instance to handle the request. + */ + val environment: Environment[E] + + /** + * Constructs a request handler with default content. + * + * @param block The block of code to invoke. + * @param request The current request. + * @tparam T The type of the data included in the handler result. + * @return A handler result. + */ + final def apply[T](block: R[AnyContent] => Future[HandlerResult[T]])(implicit request: Request[AnyContent]): Future[HandlerResult[T]] = { + invokeBlock(block) + } + + /** + * Constructs a request handler with the content of the given request. + * + * @param request The current request. + * @param block The block of code to invoke. + * @tparam B The type of the request body. + * @tparam T The type of the data included in the handler result. + * @return A handler result. + */ + final def apply[B, T](request: Request[B])(block: R[B] => Future[HandlerResult[T]]): Future[HandlerResult[T]] = { + invokeBlock(block)(request) + } + + /** + * Invoke the block. + * + * This is the main method that an request handler has to implement. + * + * @param block The block of code to invoke. + * @param request The current request. + * @tparam B The type of the request body. + * @tparam T The type of the data included in the handler result. + * @return A handler result. + */ + def invokeBlock[B, T](block: R[B] => Future[HandlerResult[T]])(implicit request: Request[B]): Future[HandlerResult[T]] + + /** + * Handles a block for an authenticator. + * + * Invokes the block with the authenticator and handles the result. See `handleInitializedAuthenticator` and + * `handleUninitializedAuthenticator` methods too see how the different authenticator types will be handled. + * + * @param authenticator An already initialized authenticator on the left and a new authenticator on the right. + * @param block The block to handle with the authenticator. + * @param request The current request header. + * @tparam T The type of the data included in the handler result. + * @return A handler result. + */ + protected def handleBlock[T](authenticator: Either[E#A, E#A], block: E#A => Future[HandlerResult[T]])(implicit request: RequestHeader) = { + authenticator match { + case Left(a) => handleInitializedAuthenticator(a, block) + case Right(a) => handleUninitializedAuthenticator(a, block) + } + } + + /** + * Handles the authentication of an identity. + * + * As first it checks for authenticators in requests, then it tries to authenticate against a request provider. + * This method marks the returned authenticators by returning already initialized authenticators on the + * left and new authenticators on the right. All new authenticators must be initialized later in the flow, + * with the result returned from the invoked block. + * + * @param request The current request. + * @tparam B The type of the request body. + * @return A tuple which consists of (maybe the existing authenticator on the left or a + * new authenticator on the right -> maybe the identity). + */ + protected def handleAuthentication[B](implicit request: Request[B]): Future[(Option[Either[E#A, E#A]], Option[E#I])] = { + environment.authenticatorService.retrieve.flatMap { + // A valid authenticator was found so we retrieve also the identity + case Some(a) if a.isValid => environment.identityService.retrieve(a.loginInfo).map(i => Some(Left(a)) -> i) + // An invalid authenticator was found so we needn't retrieve the identity + case Some(a) => Future.successful(Some(Left(a)) -> None) + // No authenticator was found so we try to authenticate with a request provider + case None => handleRequestProviderAuthentication.flatMap { + // Authentication was successful, so we retrieve the identity and create a new authenticator for it + case Some(loginInfo) => environment.identityService.retrieve(loginInfo).flatMap { i => + environment.authenticatorService.create(loginInfo).map(a => Some(Right(a)) -> i) + } + // No identity and no authenticator was found + case None => Future.successful(None -> None) + } + } + } + + /** + * Handles already initialized authenticators. + * + * The authenticator handled by this method was found in the current request. So it was initialized on + * a previous request and must now be updated if it was touched and no authenticator result was found. + * + * @param authenticator The authenticator to handle. + * @param block The block to handle with the authenticator. + * @param request The current request header. + * @tparam T The type of the data included in the handler result. + * @return A handler result. + */ + private def handleInitializedAuthenticator[T](authenticator: E#A, block: E#A => Future[HandlerResult[T]])(implicit request: RequestHeader) = { + val auth = environment.authenticatorService.touch(authenticator) + block(auth.fold(identity, identity)).flatMap { + case hr @ HandlerResult(pr: AuthenticatorResult, _) => Future.successful(hr) + case hr @ HandlerResult(pr, _) => auth match { + // Authenticator was touched so we update the authenticator and maybe the result + case Left(a) => environment.authenticatorService.update(a, pr).map(pr => hr.copy(pr)) + // Authenticator was not touched so we return the original result + case Right(a) => Future.successful(hr) + } + } + } + + /** + * Handles not initialized authenticators. + * + * The authenticator handled by this method was newly created after authentication with a request provider. + * So it must be initialized with the result of the invoked block if no authenticator result was found. + * + * @param authenticator The authenticator to handle. + * @param block The block to handle with the authenticator. + * @param request The current request header. + * @tparam T The type of the data included in the handler result. + * @return A handler result. + */ + private def handleUninitializedAuthenticator[T](authenticator: E#A, block: E#A => Future[HandlerResult[T]])(implicit request: RequestHeader) = { + block(authenticator).flatMap { + case hr @ HandlerResult(pr: AuthenticatorResult, _) => Future.successful(hr) + case hr @ HandlerResult(pr, _) => + environment.authenticatorService.init(authenticator).flatMap { value => + environment.authenticatorService.embed(value, pr) + }.map(pr => hr.copy(pr)) + } + } + + /** + * Handles the authentication with the request providers. + * + * Silhouette supports chaining of request providers. So if more as one request provider is defined + * it tries to authenticate until one provider returns an identity. The order of the providers + * isn't guaranteed. + * + * @param request The current request. + * @tparam B The type of the request body. + * @return Some identity or None if authentication was not successful. + */ + private def handleRequestProviderAuthentication[B](implicit request: Request[B]): Future[Option[LoginInfo]] = { + def auth(providers: Seq[RequestProvider]): Future[Option[LoginInfo]] = { + providers match { + case Nil => Future.successful(None) + case h :: t => h.authenticate(request).flatMap { + case Some(i) => Future.successful(Some(i)) + case None => if (t.isEmpty) Future.successful(None) else auth(t) + } + case _ => Future.successful(None) + } + } + + auth(environment.requestProviders) + } +} diff --git a/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala new file mode 100644 index 00000000..3a8f1792 --- /dev/null +++ b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala @@ -0,0 +1,408 @@ +/** + * Original work: SecureSocial (https://github.com/jaliss/securesocial) + * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss + * + * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) + * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.github.honeycombcheesecake.play.silhouette.api.actions + +import javax.inject.Inject + +import io.github.honeycombcheesecake.play.silhouette.api._ +import play.api.i18n.MessagesApi +import play.api.inject.Module +import play.api.mvc._ +import play.api.{ Configuration, Environment => PlayEnv } + +import scala.concurrent.{ ExecutionContext, Future } + +/** + * A request header that only allows access if an identity is authenticated and authorized. + * + * @tparam E The type of the environment. + */ +trait SecuredRequestHeader[E <: Env] extends RequestHeader { + /** + * @return The identity implementation. + */ + def identity: E#I + + /** + * @return The authenticator implementation. + */ + def authenticator: E#A +} + +/** + * A request that only allows access if an identity is authenticated and authorized. + * + * @tparam E The type of the environment. + * @tparam B The type of the request body. + */ +trait SecuredRequest[E <: Env, +B] extends Request[B] with SecuredRequestHeader[E] + +object SecuredRequest { + /** + * A request that only allows access if an identity is authenticated and authorized. + * + * @param identity The identity implementation. + * @param authenticator The authenticator implementation. + * @param request The current request. + * @tparam E The type of the environment. + * @tparam B The type of the request body. + */ + def apply[E <: Env, B](identity: E#I, authenticator: E#A, request: Request[B]): SecuredRequest[E, B] = { + new DefaultSecuredRequest(identity, authenticator, request) + } + + /** + * Unapply method for secured request. + * + * @param securedRequest the secured request. + * @tparam E The type of the environment. + * @tparam B The type of the request body. + */ + def unapply[E <: Env, B](securedRequest: SecuredRequest[E, B]): Option[(E#I, E#A, Request[B])] = { + securedRequest match { + case dsr: DefaultSecuredRequest[E, B] => + Some((dsr.identity, dsr.authenticator, dsr.request)) + case sr: SecuredRequest[E, B] => + Some((sr.identity, sr.authenticator, sr)) + } + } +} + +class DefaultSecuredRequest[E <: Env, B]( + val identity: E#I, + val authenticator: E#A, + val request: Request[B]) extends WrappedRequest(request) with SecuredRequest[E, B] + +/** + * Request handler builder implementation to provide the foundation for secured request handlers. + * + * @param environment The environment instance to handle the request. + * @param errorHandler The instance of the secured error handler. + * @param authorization Maybe an authorization instance. + * @tparam E The type of the environment. + */ +final case class SecuredRequestHandlerBuilder[E <: Env]( + environment: Environment[E], + errorHandler: SecuredErrorHandler, + authorization: Option[Authorization[E#I, E#A]]) + extends RequestHandlerBuilder[E, ({ type R[B] = SecuredRequest[E, B] })#R] { + + /** + * Creates a secured action handler builder with a new error handler in place. + * + * @param securedErrorHandler An error handler instance. + * @return A secured action handler builder with a new error handler in place. + */ + def apply(securedErrorHandler: SecuredErrorHandler): SecuredRequestHandlerBuilder[E] = + SecuredRequestHandlerBuilder[E](environment, securedErrorHandler, authorization) + + /** + * Creates a secured action handler builder with an authorization in place. + * + * @param specifiedAuthorization An authorization object that checks if the user is authorized to invoke the action. + * @return A secured action handler builder with an authorization in place. + */ + def apply(specifiedAuthorization: Authorization[E#I, E#A]): SecuredRequestHandlerBuilder[E] = + SecuredRequestHandlerBuilder[E](environment, errorHandler, Some(specifiedAuthorization)) + + /** + * Invokes the block. + * + * @param block The block of code to invoke. + * @param request The current request. + * @tparam B The type of the request body. + * @tparam T The type of the data included in the handler result. + * @return A handler result. + */ + override def invokeBlock[B, T](block: SecuredRequest[E, B] => Future[HandlerResult[T]])( + implicit + request: Request[B]): Future[HandlerResult[T]] = { + withAuthorization(handleAuthentication).flatMap { + // A user is both authenticated and authorized. The request will be granted + case (Some(authenticator), Some(identity), Some(authorized)) if authorized => + environment.eventBus.publish(AuthenticatedEvent(identity, request)) + handleBlock(authenticator, a => block(SecuredRequest(identity, a, request))) + // A user is authenticated but not authorized. The request will be forbidden + case (Some(authenticator), Some(identity), _) => + environment.eventBus.publish(NotAuthorizedEvent(identity, request)) + handleBlock(authenticator, _ => errorHandler.onNotAuthorized.map(r => HandlerResult(r))) + // An authenticator but no user was found. The request will ask for authentication and the authenticator will be discarded + case (Some(authenticator), None, _) => + environment.eventBus.publish(NotAuthenticatedEvent(request)) + for { + result <- errorHandler.onNotAuthenticated + discardedResult <- environment.authenticatorService.discard(authenticator.extract, result) + } yield HandlerResult(discardedResult) + // No authenticator and no user was found. The request will ask for authentication + case _ => + environment.eventBus.publish(NotAuthenticatedEvent(request)) + errorHandler.onNotAuthenticated.map(r => HandlerResult(r)) + } + } + + /** + * Adds the authorization status to the authentication result. + * + * @param result The authentication result. + * @param request The current request. + * @tparam B The type of the request body. + * @return The authentication result with the additional authorization status. + */ + private def withAuthorization[B](result: Future[(Option[Either[E#A, E#A]], Option[E#I])])(implicit request: Request[B]) = { + result.flatMap { + case (Some(a), Some(i)) => + authorization.map(_.isAuthorized(i, a.extract)).getOrElse(Future.successful(true)).map(b => (Some(a), Some(i), Some(b))) + case (a, i) => + Future.successful((a, i, None)) + } + } +} + +/** + * A secured request handler. + * + * A handler which intercepts requests and checks if there is an authenticated user. + * If there is one, the execution continues and the enclosed code is invoked. + * + * If the user is not authenticated or not authorized, the request is forwarded to + * the [[io.github.honeycombcheesecake.play.silhouette.api.actions.SecuredErrorHandler.onNotAuthenticated]] or + * the [[io.github.honeycombcheesecake.play.silhouette.api.actions.SecuredErrorHandler.onNotAuthorized]] methods. + */ +trait SecuredRequestHandler { + + /** + * The instance of the secured error handler. + */ + val errorHandler: SecuredErrorHandler + + /** + * Applies the environment to the request handler stack. + * + * @param environment The environment instance to handle the request. + * @tparam E The type of the environment. + * @return A secured request handler builder. + */ + def apply[E <: Env](environment: Environment[E]): SecuredRequestHandlerBuilder[E] +} + +/** + * Default implementation of the [[SecuredRequestHandler]]. + * + * @param errorHandler The instance of the secured error handler. + */ +class DefaultSecuredRequestHandler @Inject() (val errorHandler: SecuredErrorHandler) + extends SecuredRequestHandler { + + /** + * Applies the environment to the request handler stack. + * + * @param environment The environment instance to handle the request. + * @tparam E The type of the environment. + * @return A secured request handler builder. + */ + override def apply[E <: Env](environment: Environment[E]): SecuredRequestHandlerBuilder[E] = + SecuredRequestHandlerBuilder[E](environment, errorHandler, None) +} + +/** + * Action builder implementation to provide the foundation for secured actions. + * + * @param requestHandler The request handler instance. + * @param parser The body parser. + * @tparam E The type of the environment. + * @tparam P The type of the request body. + */ +final case class SecuredActionBuilder[E <: Env, P]( + requestHandler: SecuredRequestHandlerBuilder[E], + parser: BodyParser[P]) extends ActionBuilder[({ type R[B] = SecuredRequest[E, B] })#R, P] { + + /** + * Creates a secured action builder with a new error handler in place. + * + * @param errorHandler An error handler instance. + * @return A secured action builder. + */ + def apply(errorHandler: SecuredErrorHandler): SecuredActionBuilder[E, P] = SecuredActionBuilder[E, P](requestHandler(errorHandler), parser) + + /** + * Creates a secured action builder with an authorization in place. + * + * @param authorization An authorization object that checks if the user is authorized to invoke the action. + * @return A secured action builder. + */ + def apply(authorization: Authorization[E#I, E#A]): SecuredActionBuilder[E, P] = SecuredActionBuilder[E, P](requestHandler(authorization), parser) + + /** + * Invokes the block. + * + * @param request The current request. + * @param block The block of code to invoke. + * @tparam B The type of the request body. + * @return A handler result. + */ + override def invokeBlock[B](request: Request[B], block: SecuredRequest[E, B] => Future[Result]) = { + implicit val ec: ExecutionContext = executionContext + implicit val req: Request[B] = request + val b = (r: SecuredRequest[E, B]) => block(r).map(r => HandlerResult(r)) + + requestHandler(request)(b).map(_.result).recoverWith(requestHandler.errorHandler.exceptionHandler) + } + + /** + * Get the execution context to run the request in. + * + * @return The execution context. + */ + override protected def executionContext: ExecutionContext = requestHandler.executionContext +} + +/** + * An action based on the [[SecuredRequestHandler]]. + */ +trait SecuredAction { + + /** + * The instance of the secured request handler. + */ + val requestHandler: SecuredRequestHandler + + /** + * The default body parser. + */ + val bodyParser: BodyParsers.Default + + /** + * Applies the environment to the action stack. + * + * @param environment The environment instance to handle the request. + * @tparam E The type of the environment. + * @return A secured action builder. + */ + def apply[E <: Env](environment: Environment[E]): SecuredActionBuilder[E, AnyContent] +} + +/** + * Default implementation of the [[SecuredAction]]. + * + * @param requestHandler The instance of the secured request handler. + * @param bodyParser The default body parser. + */ +class DefaultSecuredAction @Inject() ( + val requestHandler: SecuredRequestHandler, + val bodyParser: BodyParsers.Default) extends SecuredAction { + + /** + * Applies the environment to the action stack. + * + * @param environment The environment instance to handle the request. + * @tparam E The type of the environment. + * @return A secured action builder. + */ + override def apply[E <: Env](environment: Environment[E]): SecuredActionBuilder[E, AnyContent] = + SecuredActionBuilder[E, AnyContent](requestHandler[E](environment), bodyParser) +} + +/** + * Error handler for secured actions. + */ +trait SecuredErrorHandler extends NotAuthenticatedErrorHandler with NotAuthorizedErrorHandler { + + /** + * Exception handler which chains the exceptions handlers from the sub types. + * + * @param request The request header. + * @return A partial function which maps an exception to a Play result. + */ + override def exceptionHandler(implicit request: RequestHeader): PartialFunction[Throwable, Future[Result]] = { + super[NotAuthenticatedErrorHandler].exceptionHandler orElse + super[NotAuthorizedErrorHandler].exceptionHandler + } +} + +/** + * Default implementation of the [[SecuredErrorHandler]]. + * + * @param messagesApi The Play messages API. + */ +class DefaultSecuredErrorHandler @Inject() (val messagesApi: MessagesApi) + extends SecuredErrorHandler + with DefaultNotAuthenticatedErrorHandler + with DefaultNotAuthorizedErrorHandler { + + /** + * Exception handler which chains the exceptions handlers from the sub types. + * + * @param request The request header. + * @return A partial function which maps an exception to a Play result. + */ + override def exceptionHandler(implicit request: RequestHeader): PartialFunction[Throwable, Future[Result]] = { + super[DefaultNotAuthenticatedErrorHandler].exceptionHandler orElse + super[DefaultNotAuthorizedErrorHandler].exceptionHandler + } +} + +/** + * Play module for providing the secured action components. + */ +class SecuredActionModule extends Module { + def bindings(environment: PlayEnv, configuration: Configuration) = { + Seq( + bind[SecuredAction].to[DefaultSecuredAction], + bind[SecuredRequestHandler].to[DefaultSecuredRequestHandler]) + } +} + +/** + * Play module to provide the secured error handler component. + * + * We provide an extra module so that it can be easily replaced with a custom implementation + * without to declare bindings for the other secured action module. + */ +class SecuredErrorHandlerModule extends Module { + def bindings(environment: PlayEnv, configuration: Configuration) = { + Seq( + bind[SecuredErrorHandler].to[DefaultSecuredErrorHandler]) + } +} + +/** + * Injection helper for secured action components + */ +trait SecuredActionComponents { + + def securedErrorHandler: SecuredErrorHandler + def securedBodyParser: BodyParsers.Default + + lazy val securedRequestHandler: SecuredRequestHandler = new DefaultSecuredRequestHandler(securedErrorHandler) + lazy val securedAction: SecuredAction = new DefaultSecuredAction(securedRequestHandler, securedBodyParser) +} + +/** + * Injection helper for secured error handler component. + * + * We provide an extra component so that it can be easily replaced with a custom implementation + * without to declare bindings for the other secured action component. + */ +trait SecuredErrorHandlerComponents { + + def messagesApi: MessagesApi + + lazy val securedErrorHandler: SecuredErrorHandler = new DefaultSecuredErrorHandler(messagesApi) +} diff --git a/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala new file mode 100644 index 00000000..40678961 --- /dev/null +++ b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala @@ -0,0 +1,259 @@ +/** + * Original work: SecureSocial (https://github.com/jaliss/securesocial) + * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss + * + * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) + * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.github.honeycombcheesecake.play.silhouette.api.actions + +import javax.inject.Inject + +import io.github.honeycombcheesecake.play.silhouette.api._ +import play.api.{ Configuration, Environment => PlayEnv } +import play.api.inject.Module +import play.api.mvc._ + +import scala.concurrent.{ ExecutionContext, Future } + +/** + * A request that adds maybe the identity and maybe the authenticator for the current call. + * + * @tparam E The type of the environment. + */ +trait UserAwareRequestHeader[E <: Env] extends RequestHeader { + /** + * @return Some identity implementation if authentication was successful, None otherwise. + */ + def identity: Option[E#I] + + /** + * @return Some authenticator implementation if authentication was successful, None otherwise. + */ + def authenticator: Option[E#A] +} + +trait UserAwareRequest[E <: Env, +B] extends Request[B] with UserAwareRequestHeader[E] + +object UserAwareRequest { + + /** + * A request that adds maybe the identity and maybe the authenticator for the current call. + * + * @param identity Some identity implementation if authentication was successful, None otherwise. + * @param authenticator Some authenticator implementation if authentication was successful, None otherwise. + * @param request The current request. + * @tparam E The type of the environment. + * @tparam B The type of the request body. + */ + def apply[E <: Env, B]( + identity: Option[E#I], + authenticator: Option[E#A], + request: Request[B]): UserAwareRequest[E, B] = { + new DefaultUserAwareRequest(identity, authenticator, request) + } + + /** + * Unapply method for user aware request. + * + * @param userAwareRequest the user aware request. + * @tparam E The type of the environment. + * @tparam B The type of the request body. + */ + def unapply[E <: Env, B](userAwareRequest: UserAwareRequest[E, B]): Option[(Option[E#I], Option[E#A], Request[B])] = { + userAwareRequest match { + case duar: DefaultUserAwareRequest[E, B] => + Some((duar.identity, duar.authenticator, duar.request)) + case uar: UserAwareRequest[E, B] => + Some((uar.identity, uar.authenticator, uar)) + } + } +} + +class DefaultUserAwareRequest[E <: Env, B]( + val identity: Option[E#I], + val authenticator: Option[E#A], + val request: Request[B]) extends WrappedRequest(request) with UserAwareRequest[E, B] + +/** + * Request handler builder implementation to provide the foundation for user-aware request handlers. + * + * @param environment The environment instance to handle the request. + * @tparam E The type of the environment. + */ +final case class UserAwareRequestHandlerBuilder[E <: Env](environment: Environment[E]) + extends RequestHandlerBuilder[E, ({ type R[B] = UserAwareRequest[E, B] })#R] { + + /** + * Invokes the block. + * + * @param block The block of code to invoke. + * @param request The current request. + * @tparam B The type of the request body. + * @tparam T The type of the data included in the handler result. + * @return A handler result. + */ + override def invokeBlock[B, T](block: UserAwareRequest[E, B] => Future[HandlerResult[T]])(implicit request: Request[B]): Future[HandlerResult[T]] = { + handleAuthentication.flatMap { + // A valid authenticator was found and the identity may be exists + case (Some(authenticator), identity) if authenticator.extract.isValid => + handleBlock(authenticator, a => block(UserAwareRequest(identity, Some(a), request))) + // An invalid authenticator was found. The authenticator will be discarded + case (Some(authenticator), _) if !authenticator.extract.isValid => + block(UserAwareRequest(None, None, request)).flatMap { + case hr @ HandlerResult(pr, _) => + environment.authenticatorService.discard(authenticator.extract, pr).map(r => hr.copy(r)) + } + // No authenticator and no user was found + case _ => block(UserAwareRequest(None, None, request)) + } + } +} + +/** + * A user-aware request handler. + * + * A handler that can be used for endpoints that need to know if there is a current user but + * can be executed even if there isn't one. + */ +trait UserAwareRequestHandler { + + /** + * Applies the environment to the request handler stack. + * + * @param environment The environment instance to handle the request. + * @tparam E The type of the environment. + * @return A user-aware request handler builder. + */ + def apply[E <: Env](environment: Environment[E]): UserAwareRequestHandlerBuilder[E] +} + +/** + * Default implementation of the [[UserAwareRequestHandler]]. + */ +class DefaultUserAwareRequestHandler extends UserAwareRequestHandler { + + /** + * Applies the environment to the request handler stack. + * + * @param environment The environment instance to handle the request. + * @tparam E The type of the environment. + * @return A user-aware request handler builder. + */ + override def apply[E <: Env](environment: Environment[E]): UserAwareRequestHandlerBuilder[E] = UserAwareRequestHandlerBuilder[E](environment) +} + +/** + * Action builder implementation to provide the foundation for user-aware actions. + * + * @param requestHandler The request handler instance. + * @param parser The body parser. + * @tparam E The type of the environment. + * @tparam P The type of the request body. + */ +final case class UserAwareActionBuilder[E <: Env, P]( + requestHandler: UserAwareRequestHandlerBuilder[E], + parser: BodyParser[P]) extends ActionBuilder[({ type R[B] = UserAwareRequest[E, B] })#R, P] { + + /** + * Invokes the block. + * + * @param request The current request. + * @param block The block of code to invoke. + * @tparam B The type of the request body. + * @return The result to send to the client. + */ + override def invokeBlock[B](request: Request[B], block: UserAwareRequest[E, B] => Future[Result]) = { + implicit val ec: ExecutionContext = executionContext + requestHandler(request) { req => + block(req).map(r => HandlerResult(r)) + }.map(_.result) + } + + /** + * Get the execution context to run the request in. + * + * @return The execution context. + */ + override protected def executionContext: ExecutionContext = requestHandler.executionContext +} + +/** + * An action based on the [[UserAwareRequestHandler]]. + */ +trait UserAwareAction { + + /** + * The instance of the user-aware request handler. + */ + val requestHandler: UserAwareRequestHandler + + /** + * The default body parser. + */ + val bodyParser: BodyParsers.Default + + /** + * Applies the environment to the action stack. + * + * @param environment The environment instance to handle the request. + * @tparam E The type of the environment. + * @return A user-aware action builder. + */ + def apply[E <: Env](environment: Environment[E]): UserAwareActionBuilder[E, AnyContent] +} + +/** + * Default implementation of the [[UserAwareAction]]. + * + * @param requestHandler The instance of the user-aware request handler. + * @param bodyParser The default body parser. + */ +class DefaultUserAwareAction @Inject() ( + val requestHandler: UserAwareRequestHandler, + val bodyParser: BodyParsers.Default) extends UserAwareAction { + + /** + * Applies the environment to the action stack. + * + * @param environment The environment instance to handle the request. + * @tparam E The type of the environment. + * @return A user-aware action builder. + */ + override def apply[E <: Env](environment: Environment[E]): UserAwareActionBuilder[E, AnyContent] = + UserAwareActionBuilder[E, AnyContent](requestHandler[E](environment), bodyParser) +} + +/** + * Play module for providing the user-aware action components. + */ +class UserAwareActionModule extends Module { + def bindings(environment: PlayEnv, configuration: Configuration) = { + Seq( + bind[UserAwareAction].to[DefaultUserAwareAction], + bind[UserAwareRequestHandler].to[DefaultUserAwareRequestHandler]) + } +} + +/** + * Injection helper for user-aware action components + */ +trait UserAwareActionComponents { + + def userAwareBodyParser: BodyParsers.Default + + lazy val userAwareRequestHandler: UserAwareRequestHandler = new DefaultUserAwareRequestHandler() + lazy val userAwareAction: UserAwareAction = new DefaultUserAwareAction(userAwareRequestHandler, userAwareBodyParser) +} diff --git a/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala new file mode 100644 index 00000000..efc17d93 --- /dev/null +++ b/silhouette/app-2/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala @@ -0,0 +1,226 @@ +/** + * Original work: SecureSocial (https://github.com/jaliss/securesocial) + * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss + * + * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) + * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.github.honeycombcheesecake.play.silhouette.api.services + +import io.github.honeycombcheesecake.play.silhouette.api.util.{ ExtractableRequest, ExecutionContextProvider } +import io.github.honeycombcheesecake.play.silhouette.api.{ Authenticator, LoginInfo } +import play.api.http.HttpEntity +import play.api.libs.typedmap.TypedMap +import play.api.mvc._ + +import scala.concurrent.Future + +/** + * A marker result which indicates that an operation on an authenticator was processed and + * therefore it shouldn't updated automatically. + * + * Due the fact that the update method gets called on every subsequent request to update the + * authenticator related data in the backing store and in the result, it isn't possible to + * discard or renew the authenticator simultaneously. This is because the "update" method would + * override the result created by the "renew" or "discard" method, because it will be executed + * as last in the chain. + * + * As example: + * If we discard the session in a Silhouette action then it will be removed from session. But + * at the end the update method will embed the session again, because it gets called with the + * result of the action. + * + * @param result The result to wrap. + */ +class AuthenticatorResult(result: Result) + extends Result(result.header, result.body, result.newSession, result.newFlash, result.newCookies, result.attrs) { + + /** + * Creates a new copy of a `AuthenticatorResult`. + * + * @param header The response header, which contains status code and HTTP headers. + * @param body The response body. + * @param newSession A new session. + * @param newFlash A new flash. + * @param newCookies Some new cookies. + * @return A copy of a `AuthenticatorResult`. + */ + override def copy( + header: ResponseHeader, + body: HttpEntity, + newSession: Option[Session], + newFlash: Option[Flash], + newCookies: Seq[Cookie], + attrs: TypedMap) = { + AuthenticatorResult(super.copy(header, body, newSession, newFlash, newCookies, attrs)) + } +} + +/** + * The companion object. + */ +object AuthenticatorResult { + + /** + * Instantiates a new authenticator result. + * + * @param result The result to wrap. + * @return An authenticator result. + */ + def apply(result: Result) = new AuthenticatorResult(result) +} + +/** + * Handles authenticators for the Silhouette module. + * + * @tparam T The type of the authenticator this service is responsible for. + */ +trait AuthenticatorService[T <: Authenticator] extends ExecutionContextProvider { + + /** + * Creates a new authenticator for the specified login info. + * + * @param loginInfo The login info for which the authenticator should be created. + * @param request The request header. + * @return An authenticator. + */ + def create(loginInfo: LoginInfo)(implicit request: RequestHeader): Future[T] + + /** + * Retrieves the authenticator from request. + * + * @param request The request to retrieve the authenticator from. + * @tparam B The type of the request body. + * @return Some authenticator or None if no authenticator could be found in request. + */ + def retrieve[B](implicit request: ExtractableRequest[B]): Future[Option[T]] + + /** + * Initializes an authenticator and instead of embedding into the the request or result, it returns + * the serialized value. + * + * @param authenticator The authenticator instance. + * @param request The request header. + * @return The serialized authenticator value. + */ + def init(authenticator: T)(implicit request: RequestHeader): Future[T#Value] + + /** + * Embeds authenticator specific artifacts into the response. + * + * @param value The authenticator value to embed. + * @param result The result to manipulate. + * @param request The request header. + * @return The manipulated result. + */ + def embed(value: T#Value, result: Result)(implicit request: RequestHeader): Future[AuthenticatorResult] + + /** + * Embeds authenticator specific artifacts into the request. + * + * This method can be used to embed an authenticator in a existing request. This can be useful + * in Play filters. So before executing a SecuredAction we can embed the authenticator in + * the request to lead the action to believe that the request is a new request which contains + * a valid authenticator. + * + * If an existing authenticator exists, then it will be overridden. + * + * @param value The authenticator value to embed. + * @param request The request header. + * @return The manipulated request header. + */ + def embed(value: T#Value, request: RequestHeader): RequestHeader + + /** + * Touches an authenticator. + * + * An authenticator can use sliding window expiration. This means that the authenticator times + * out after a certain time if it wasn't used. So to mark an authenticator as used it will be + * touched on every request to a Silhouette action. If an authenticator should not be touched + * because of the fact that sliding window expiration is disabled, then it should be returned + * on the right, otherwise it should be returned on the left. An untouched authenticator needn't + * be updated later by the [[update]] method. + * + * @param authenticator The authenticator to touch. + * @return The touched authenticator on the left or the untouched authenticator on the right. + */ + def touch(authenticator: T): Either[T, T] + + /** + * Updates a touched authenticator. + * + * If the authenticator was updated, then the updated artifacts should be embedded into the response. + * This method gets called on every subsequent request if an identity accesses a Silhouette action, + * expect the authenticator was not touched. + * + * @param authenticator The authenticator to update. + * @param result The result to manipulate. + * @param request The request header. + * @return The original or a manipulated result. + */ + def update(authenticator: T, result: Result)(implicit request: RequestHeader): Future[AuthenticatorResult] + + /** + * Renews the expiration of an authenticator without embedding it into the result. + * + * Based on the implementation, the renew method should revoke the given authenticator first, before + * creating a new one. If the authenticator was updated, then the updated artifacts should be returned. + * + * @param authenticator The authenticator to renew. + * @param request The request header. + * @return The serialized expression of the authenticator. + */ + def renew(authenticator: T)(implicit request: RequestHeader): Future[T#Value] + + /** + * Renews the expiration of an authenticator. + * + * Based on the implementation, the renew method should revoke the given authenticator first, before + * creating a new one. If the authenticator was updated, then the updated artifacts should be embedded + * into the response. + * + * @param authenticator The authenticator to renew. + * @param result The result to manipulate. + * @param request The request header. + * @return The original or a manipulated result. + */ + def renew(authenticator: T, result: Result)(implicit request: RequestHeader): Future[AuthenticatorResult] + + /** + * Manipulates the response and removes authenticator specific artifacts before sending it to the client. + * + * @param authenticator The authenticator instance. + * @param result The result to manipulate. + * @param request The request header. + * @return The manipulated result. + */ + def discard(authenticator: T, result: Result)(implicit request: RequestHeader): Future[AuthenticatorResult] +} + +/** + * The companion object. + */ +object AuthenticatorService { + + /** + * The error messages. + */ + val CreateError = "[Silhouette][%s] Could not create authenticator for login info: %s" + val RetrieveError = "[Silhouette][%s] Could not retrieve authenticator" + val InitError = "[Silhouette][%s] Could not init authenticator: %s" + val UpdateError = "[Silhouette][%s] Could not update authenticator: %s" + val RenewError = "[Silhouette][%s] Could not renew authenticator: %s" + val DiscardError = "[Silhouette][%s] Could not discard authenticator: %s" +} diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala similarity index 100% rename from silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala rename to silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/Authenticator.scala diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala similarity index 100% rename from silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala rename to silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/Environment.scala diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala similarity index 100% rename from silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala rename to silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/RequestHandler.scala diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala similarity index 100% rename from silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala rename to silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/actions/SecuredAction.scala diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala similarity index 100% rename from silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala rename to silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/actions/UserAwareAction.scala diff --git a/silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala b/silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala similarity index 100% rename from silhouette/app/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala rename to silhouette/app-3/io/github/honeycombcheesecake/play/silhouette/api/services/AuthenticatorService.scala diff --git a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala index c60b1fb9..72892d38 100644 --- a/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala +++ b/silhouette/test/io/github/honeycombcheesecake/play/silhouette/impl/providers/custom/FacebookProviderSpec.scala @@ -27,7 +27,7 @@ import play.api.libs.json.{ JsValue, Json } import play.api.mvc.AnyContentAsEmpty import play.api.test.{ FakeRequest, WithApplication } import test.Helper -import org.mockito.Mockito.* +import org.mockito.Mockito._ import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyString From 7fe2b61f7ba30462efd4553d707702e15fe8d3d8 Mon Sep 17 00:00:00 2001 From: MathisGuillet1 Date: Wed, 29 Nov 2023 11:24:20 +0100 Subject: [PATCH 26/30] Build cleanup --- build.sbt | 48 +++++++++++++++--------------------------------- 1 file changed, 15 insertions(+), 33 deletions(-) diff --git a/build.sbt b/build.sbt index 42abf865..2a12706b 100644 --- a/build.sbt +++ b/build.sbt @@ -19,17 +19,22 @@ ThisBuild / organizationName := "honeycomb-cheesecake" ThisBuild / scalaVersion := scala213 ThisBuild / versionScheme := Some("early-semver") ThisBuild / scalacOptions ++= Seq( - //"-unchecked", - //"-deprecation", "-feature", - //"-encoding", "utf8", - "-Xfatal-warnings", - //"-Xlint", - //"-Xlint:adapted-args", - //"-Xlint:inaccessible", - //"-Xlint:infer-any", - //"-Xlint:nullary-unit" -) + "-Xfatal-warnings" +) ++ + (CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, _)) => Seq( + "-encoding", "utf8", + "-unchecked", + "-deprecation", + "-Xlint", + "-Xlint:adapted-args", + "-Xlint:inaccessible", + "-Xlint:infer-any", + "-Xlint:nullary-unit" + ) + case _ => Seq() + }) ThisBuild / Test / scalacOptions ~= { options: Seq[String] => // Allow dead code in tests (to support using mockito). options filterNot (_ == "-Ywarn-dead-code") @@ -100,10 +105,6 @@ lazy val root = (project in file(".")) lazy val silhouette = (project in file("silhouette")) .settings( name := "play-silhouette", - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"), - dependencyUpdatesFilter -= moduleFilter(organization = "com.typesafe.akka", name = "akka-testkit"), - dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), - dependencyUpdatesFilter -= moduleFilter(organization = "om.auth0", name = "java-jwt"), libraryDependencies ++= Library.updates ++ Seq( Library.Play.cache, @@ -126,8 +127,6 @@ lazy val silhouetteCas = (project in file("silhouette-cas")) .settings( name := "play-silhouette-cas", dependencyUpdatesFailBuild := false, - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"), - dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), libraryDependencies ++= Library.updates ++ Seq( Library.casClient, @@ -144,8 +143,6 @@ lazy val silhouetteTotp = (project in file("silhouette-totp")) .settings( name := "play-silhouette-totp", dependencyUpdatesFailBuild := false, - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"), - dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), libraryDependencies ++= Library.updates ++ Seq( Library.googleAuth, @@ -158,11 +155,6 @@ lazy val silhouetteCryptoJca = (project in file("silhouette-crypto-jca")) .settings( name := "play-silhouette-crypto-jca", dependencyUpdatesFailBuild := false, - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"), - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"), - dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), - dependencyUpdatesFilter -= moduleFilter(organization = "commons-codec", name = "commons-codec"), - libraryDependencies ++= Library.updates ++ Seq( Library.commonsCodec, @@ -176,8 +168,6 @@ lazy val silhouetteArgon2 = (project in file("silhouette-password-argon2")) .settings( name := "play-silhouette-password-argon2", dependencyUpdatesFailBuild := false, - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"), - dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), libraryDependencies ++= Library.updates ++ Seq( Library.argon2, @@ -190,8 +180,6 @@ lazy val silhouetteBcrypt = (project in file("silhouette-password-bcrypt")) .settings( name := "play-silhouette-password-bcrypt", dependencyUpdatesFailBuild := false, - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"), - dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), libraryDependencies ++= Library.updates ++ Seq( Library.jbcrypt, @@ -204,9 +192,6 @@ lazy val silhouettePersistence = (project in file("silhouette-persistence")) .settings( name := "play-silhouette-persistence", dependencyUpdatesFailBuild := false, - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-core"), - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"), - dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), libraryDependencies ++= Library.updates ++ Seq( Library.Specs2.core % Test, @@ -221,9 +206,6 @@ lazy val silhouetteTestkit = (project in file("silhouette-testkit")) .settings( name := "play-silhouette-testkit", dependencyUpdatesFailBuild := false, - dependencyUpdatesFilter -= moduleFilter(organization = "org.specs2", name = "specs2-matcher-extra"), - dependencyUpdatesFilter -= moduleFilter(organization = "com.typesafe.akka", name = "akka-testkit"), - dependencyUpdatesFilter -= moduleFilter(organization = "commons-io", name = "commons-io"), libraryDependencies ++= Library.updates ++ Seq( Library.Play.test, From a63b0bb1650b92037ead0c73bc8a96de3a46a9ee Mon Sep 17 00:00:00 2001 From: MathisGuillet1 Date: Wed, 29 Nov 2023 11:56:59 +0100 Subject: [PATCH 27/30] Disable Xlint-all flag --- build.sbt | 1 - 1 file changed, 1 deletion(-) diff --git a/build.sbt b/build.sbt index 2a12706b..7d191a36 100644 --- a/build.sbt +++ b/build.sbt @@ -27,7 +27,6 @@ ThisBuild / scalacOptions ++= Seq( "-encoding", "utf8", "-unchecked", "-deprecation", - "-Xlint", "-Xlint:adapted-args", "-Xlint:inaccessible", "-Xlint:infer-any", From b56e1d3864889dabe806afdebc555f683374e4e1 Mon Sep 17 00:00:00 2001 From: MathisGuillet1 Date: Wed, 29 Nov 2023 12:21:04 +0100 Subject: [PATCH 28/30] Switch default to scala 3 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 7d191a36..0ef93464 100644 --- a/build.sbt +++ b/build.sbt @@ -16,7 +16,7 @@ ThisBuild / Test / publishArtifact := false ThisBuild / pomIncludeRepository := { _ => false } ThisBuild / organization := "io.github.honeycomb-cheesecake" ThisBuild / organizationName := "honeycomb-cheesecake" -ThisBuild / scalaVersion := scala213 +ThisBuild / scalaVersion := scala3 ThisBuild / versionScheme := Some("early-semver") ThisBuild / scalacOptions ++= Seq( "-feature", From df8b5f39f07229391898028cb3faaa873f57e010 Mon Sep 17 00:00:00 2001 From: MathisGuillet1 Date: Wed, 29 Nov 2023 12:34:33 +0100 Subject: [PATCH 29/30] Switch back default to scala 2 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 0ef93464..7d191a36 100644 --- a/build.sbt +++ b/build.sbt @@ -16,7 +16,7 @@ ThisBuild / Test / publishArtifact := false ThisBuild / pomIncludeRepository := { _ => false } ThisBuild / organization := "io.github.honeycomb-cheesecake" ThisBuild / organizationName := "honeycomb-cheesecake" -ThisBuild / scalaVersion := scala3 +ThisBuild / scalaVersion := scala213 ThisBuild / versionScheme := Some("early-semver") ThisBuild / scalacOptions ++= Seq( "-feature", From eed5074d26ca480be2544ae786e875be3e9c40a8 Mon Sep 17 00:00:00 2001 From: Matthias Kurz Date: Fri, 8 Dec 2023 10:56:37 +0100 Subject: [PATCH 30/30] Remove (unused) helpers that rely on Around Because Around relies on DelayedInit which was dropped in Scala 3 --- silhouette/test/Helpers.scala | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/silhouette/test/Helpers.scala b/silhouette/test/Helpers.scala index 45f36c18..da261fd7 100644 --- a/silhouette/test/Helpers.scala +++ b/silhouette/test/Helpers.scala @@ -19,7 +19,6 @@ import io.github.honeycombcheesecake.play.silhouette.api.AuthInfo import io.github.honeycombcheesecake.play.silhouette.impl.providers.{ SocialProfile, SocialStateItem, StatefulAuthInfo } import org.specs2.execute.{ AsResult, Result => Specs2Result } import org.specs2.matcher.{ JsonMatchers, MatchResult } -import org.specs2.mutable.Around import play.api.libs.json.{ JsValue, Json } import play.api.mvc.{ Result => PlayResult } import play.api.test.PlaySpecification @@ -29,37 +28,6 @@ import scala.io.{ Codec, Source } import scala.reflect.ClassTag import org.mockito.Mockito -/** - * Executes a before method in the context of the around method. - */ -trait BeforeWithinAround extends Around { - def before: Any - abstract override def around[T: AsResult](t: => T): Specs2Result = super.around { - before; t - } -} - -/** - * Executes an after method in the context of the around method. - */ -trait AfterWithinAround extends Around { - def after: Any - abstract override def around[T: AsResult](t: => T): Specs2Result = super.around { - try { t } finally { after } - } -} - -/** - * Executes before and after methods in the context of the around method. - */ -trait BeforeAfterWithinAround extends Around { - def before: Any - def after: Any - abstract override def around[T: AsResult](t: => T): Specs2Result = super.around { - try { before; t } finally { after } - } -} - /** * Base test case for the social providers. */