From 24f7b278ef5c690abe87fac6beefd279356715e0 Mon Sep 17 00:00:00 2001 From: akkie Date: Fri, 30 Oct 2015 21:36:54 +0100 Subject: [PATCH] Move persistence based API into a separate project It's often misleading that the persistence based API is integrated into the core project, because it's not really required by the core. The provides API is only one possible implementation which helps to start a new application quicker. Therefore this pull request moves the persistence based API into a separate project and provides a in-memory implementation for the auth info implementations. It's now also possible to provide different drop-in replacements for the persistence layer instead of creating a separate seed template for every persistence type(reactivemongo, slick, ...). --- project/Build.scala | 19 +- project/Dependencies.scala | 9 +- silhouette-persistence-memory/build.sbt | 8 + .../persistence/daos/OAuth1InfoDAO.scala | 25 +++ .../persistence/daos/OAuth2InfoDAO.scala | 25 +++ .../persistence/daos/OpenIDInfoDAO.scala | 25 +++ .../persistence/daos/PasswordInfoDAO.scala | 25 +++ .../silhouette/persistence/daos/package.scala | 21 +++ .../modules/PersistenceModule.scala | 61 ++++++ .../persistence/daos/OAuth1InfoDAOSpec.scala | 103 ++++++++++ .../persistence/daos/OAuth2InfoDAOSpec.scala | 112 +++++++++++ .../persistence/daos/OpenIDInfoDAOSpec.scala | 107 +++++++++++ .../daos/PasswordInfoDAOSpec.scala | 107 +++++++++++ silhouette-persistence/build.sbt | 10 + .../persistence}/daos/AuthInfoDAO.scala | 2 +- .../daos/DelegableAuthInfoDAO.scala | 8 +- .../daos/InMemoryAuthInfoDAO.scala | 97 ++++++++++ .../persistence}/daos/package.scala | 2 +- .../CacheAuthenticatorRepository.scala | 9 +- .../DelegableAuthInfoRepository.scala | 27 +-- .../persistence}/repositories/package.scala | 2 +- .../daos/InMemoryAuthInfoDAOSpec.scala | 107 +++++++++++ .../CacheAuthenticatorRepositorySpec.scala | 23 +-- .../DelegableAuthInfoRepositorySpec.scala | 178 ++++++------------ .../mohiva/play/silhouette/test/Fakes.scala | 8 +- silhouette-testkit/build.sbt | 4 +- .../play/silhouette/test/FakesSpec.scala | 18 +- .../AuthenticatorRepository.scala} | 6 +- .../BearerTokenAuthenticator.scala | 16 +- .../authenticators/CookieAuthenticator.scala | 16 +- .../authenticators/JWTAuthenticator.scala | 16 +- silhouette/build.sbt | 4 +- .../BearerTokenAuthenticatorSpec.scala | 48 ++--- .../CookieAuthenticatorSpec.scala | 130 ++++++------- .../authenticators/JWTAuthenticatorSpec.scala | 96 +++++----- 35 files changed, 1131 insertions(+), 343 deletions(-) create mode 100644 silhouette-persistence-memory/build.sbt create mode 100644 silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/OAuth1InfoDAO.scala create mode 100644 silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/OAuth2InfoDAO.scala create mode 100644 silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/OpenIDInfoDAO.scala create mode 100644 silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/PasswordInfoDAO.scala create mode 100644 silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/package.scala create mode 100644 silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/modules/PersistenceModule.scala create mode 100644 silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/OAuth1InfoDAOSpec.scala create mode 100644 silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/OAuth2InfoDAOSpec.scala create mode 100644 silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/OpenIDInfoDAOSpec.scala create mode 100644 silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/PasswordInfoDAOSpec.scala create mode 100644 silhouette-persistence/build.sbt rename {silhouette/app/com/mohiva/play/silhouette/impl => silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence}/daos/AuthInfoDAO.scala (97%) rename {silhouette/app/com/mohiva/play/silhouette/impl => silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence}/daos/DelegableAuthInfoDAO.scala (80%) create mode 100644 silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/InMemoryAuthInfoDAO.scala rename {silhouette/app/com/mohiva/play/silhouette/impl => silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence}/daos/package.scala (93%) rename silhouette/app/com/mohiva/play/silhouette/impl/daos/CacheAuthenticatorDAO.scala => silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/repositories/CacheAuthenticatorRepository.scala (83%) rename {silhouette/app/com/mohiva/play/silhouette/impl => silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence}/repositories/DelegableAuthInfoRepository.scala (79%) rename {silhouette/app/com/mohiva/play/silhouette/impl => silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence}/repositories/package.scala (93%) create mode 100644 silhouette-persistence/src/test/scala/com/mohiva/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala rename silhouette/test/com/mohiva/play/silhouette/impl/daos/CacheAuthenticatorDAOSpec.scala => silhouette-persistence/src/test/scala/com/mohiva/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala (76%) rename {silhouette/test/com/mohiva/play/silhouette/impl => silhouette-persistence/src/test/scala/com/mohiva/play/silhouette/persistence}/repositories/DelegableAuthInfoRepositorySpec.scala (56%) rename silhouette/app/com/mohiva/play/silhouette/{impl/daos/AuthenticatorDAO.scala => api/repositories/AuthenticatorRepository.scala} (88%) diff --git a/project/Build.scala b/project/Build.scala index e3d93ca2d..8ccbfa8cc 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -24,6 +24,18 @@ object Build extends Build { base = file("silhouette") ) + val silhouettePersistence = Project( + id = "play-silhouette-persistence", + base = file("silhouette-persistence"), + dependencies = Seq(silhouette) + ) + + val silhouettePersistenceMemory = Project( + id = "play-silhouette-persistence-memory", + base = file("silhouette-persistence-memory"), + dependencies = Seq(silhouettePersistence) + ) + val silhouetteTestkit = Project( id = "play-silhouette-testkit", base = file("silhouette-testkit"), @@ -33,7 +45,12 @@ object Build extends Build { val root = Project( id = "root", base = file("."), - aggregate = Seq(silhouette, silhouetteTestkit), + aggregate = Seq( + silhouette, + silhouettePersistence, + silhouettePersistenceMemory, + silhouetteTestkit + ), settings = Defaults.coreDefaultSettings ++ APIDoc.settings ++ Seq( diff --git a/project/Dependencies.scala b/project/Dependencies.scala index fbc67199e..c9f047273 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -35,10 +35,16 @@ object Dependencies { val cache = "com.typesafe.play" %% "play-cache" % version val test = "com.typesafe.play" %% "play-test" % version val specs2 = "com.typesafe.play" %% "play-specs2" % version + object Specs2 { + private val version = "3.4" + val matcherExtra = "org.specs2" %% "specs2-matcher-extra" % version + val mock = "org.specs2" %% "specs2-mock" % version + } } object Specs2 { - private val version = "3.4" + private val version = "3.6.5" + val core = "org.specs2" %% "specs2-core" % version val matcherExtra = "org.specs2" %% "specs2-matcher-extra" % version val mock = "org.specs2" %% "specs2-mock" % version } @@ -46,7 +52,6 @@ object Dependencies { val jbcrypt = "org.mindrot" % "jbcrypt" % "0.3m" val jwtCore = "com.atlassian.jwt" % "jwt-core" % "1.2.4" val jwtApi = "com.atlassian.jwt" % "jwt-api" % "1.2.4" - val mockito = "org.mockito" % "mockito-core" % "1.10.19" val scalaGuice = "net.codingwell" %% "scala-guice" % "4.0.0" val akkaTestkit = "com.typesafe.akka" %% "akka-testkit" % "2.3.10" } diff --git a/silhouette-persistence-memory/build.sbt b/silhouette-persistence-memory/build.sbt new file mode 100644 index 000000000..e2095abc9 --- /dev/null +++ b/silhouette-persistence-memory/build.sbt @@ -0,0 +1,8 @@ +import Dependencies._ + +libraryDependencies ++= Seq( + Library.scalaGuice, + Library.Specs2.core % Test +) + +enablePlugins(Doc) diff --git a/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/OAuth1InfoDAO.scala b/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/OAuth1InfoDAO.scala new file mode 100644 index 000000000..cdf5b5036 --- /dev/null +++ b/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/OAuth1InfoDAO.scala @@ -0,0 +1,25 @@ +/** + * 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 com.mohiva.play.silhouette.persistence.daos + +import com.mohiva.play.silhouette.impl.providers.OAuth1Info + +/** + * The DAO to persist the OAuth1 information. + * + * Note: Not thread safe, demo only. + */ +class OAuth1InfoDAO extends InMemoryAuthInfoDAO[OAuth1Info] diff --git a/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/OAuth2InfoDAO.scala b/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/OAuth2InfoDAO.scala new file mode 100644 index 000000000..d8fe427d8 --- /dev/null +++ b/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/OAuth2InfoDAO.scala @@ -0,0 +1,25 @@ +/** + * 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 com.mohiva.play.silhouette.persistence.daos + +import com.mohiva.play.silhouette.impl.providers.OAuth2Info + +/** + * The DAO to persist the OAuth2 information. + * + * Note: Not thread safe, demo only. + */ +class OAuth2InfoDAO extends InMemoryAuthInfoDAO[OAuth2Info] diff --git a/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/OpenIDInfoDAO.scala b/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/OpenIDInfoDAO.scala new file mode 100644 index 000000000..43eadbaee --- /dev/null +++ b/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/OpenIDInfoDAO.scala @@ -0,0 +1,25 @@ +/** + * 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 com.mohiva.play.silhouette.persistence.daos + +import com.mohiva.play.silhouette.impl.providers.OpenIDInfo + +/** + * The DAO to persist the OpenID information. + * + * Note: Not thread safe, demo only. + */ +class OpenIDInfoDAO extends InMemoryAuthInfoDAO[OpenIDInfo] diff --git a/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/PasswordInfoDAO.scala b/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/PasswordInfoDAO.scala new file mode 100644 index 000000000..37ca210d6 --- /dev/null +++ b/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/PasswordInfoDAO.scala @@ -0,0 +1,25 @@ +/** + * 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 com.mohiva.play.silhouette.persistence.daos + +import com.mohiva.play.silhouette.api.util.PasswordInfo + +/** + * The DAO to persist the password information. + * + * Note: Not thread safe, demo only. + */ +class PasswordInfoDAO extends InMemoryAuthInfoDAO[PasswordInfo] diff --git a/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/package.scala b/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/package.scala new file mode 100644 index 000000000..cbddd8b12 --- /dev/null +++ b/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/daos/package.scala @@ -0,0 +1,21 @@ +/** + * 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 com.mohiva.play.silhouette.persistence + +/** + * Provides drop-in DAO replacements for the common `AuthInfo` implementations. + */ +package object daos diff --git a/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/modules/PersistenceModule.scala b/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/modules/PersistenceModule.scala new file mode 100644 index 000000000..000ef27c1 --- /dev/null +++ b/silhouette-persistence-memory/src/main/scala/com/mohiva/play/silhouette/persistence/modules/PersistenceModule.scala @@ -0,0 +1,61 @@ +/** + * 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 com.mohiva.play.silhouette.persistence.modules + +import com.google.inject.{ AbstractModule, Provides } +import com.mohiva.play.silhouette.api.repositories.AuthInfoRepository +import com.mohiva.play.silhouette.api.util.PasswordInfo +import com.mohiva.play.silhouette.impl.providers.{ OAuth1Info, OAuth2Info, OpenIDInfo } +import com.mohiva.play.silhouette.persistence.daos._ +import com.mohiva.play.silhouette.persistence.repositories.DelegableAuthInfoRepository +import net.codingwell.scalaguice.ScalaModule + +import scala.concurrent.ExecutionContext.Implicits.global + +/** + * Provides Guice bindings for the persistence module. + */ +class PersistenceModule extends AbstractModule with ScalaModule { + + /** + * Configures the module. + */ + def configure(): Unit = { + bind[DelegableAuthInfoDAO[PasswordInfo]].to[PasswordInfoDAO] + bind[DelegableAuthInfoDAO[OAuth1Info]].to[OAuth1InfoDAO] + bind[DelegableAuthInfoDAO[OAuth2Info]].to[OAuth2InfoDAO] + bind[DelegableAuthInfoDAO[OpenIDInfo]].to[OpenIDInfoDAO] + } + + /** + * Provides the auth info repository. + * + * @param passwordInfoDAO The implementation of the delegable password auth info DAO. + * @param oauth1InfoDAO The implementation of the delegable OAuth1 auth info DAO. + * @param oauth2InfoDAO The implementation of the delegable OAuth2 auth info DAO. + * @param openIDInfoDAO The implementation of the delegable OpenID auth info DAO. + * @return The auth info repository instance. + */ + @Provides + def provideAuthInfoRepository( + passwordInfoDAO: DelegableAuthInfoDAO[PasswordInfo], + oauth1InfoDAO: DelegableAuthInfoDAO[OAuth1Info], + oauth2InfoDAO: DelegableAuthInfoDAO[OAuth2Info], + openIDInfoDAO: DelegableAuthInfoDAO[OpenIDInfo]): AuthInfoRepository = { + + new DelegableAuthInfoRepository(passwordInfoDAO, oauth1InfoDAO, oauth2InfoDAO, openIDInfoDAO) + } +} diff --git a/silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/OAuth1InfoDAOSpec.scala b/silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/OAuth1InfoDAOSpec.scala new file mode 100644 index 000000000..f0de82f21 --- /dev/null +++ b/silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/OAuth1InfoDAOSpec.scala @@ -0,0 +1,103 @@ +/** + * 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 com.mohiva.play.silhouette.persistence.daos + +import com.mohiva.play.silhouette.api.LoginInfo +import com.mohiva.play.silhouette.impl.providers.OAuth1Info +import org.specs2.concurrent.ExecutionEnv +import org.specs2.control.NoLanguageFeatures +import org.specs2.mutable.Specification +import org.specs2.specification.Scope + +import scala.concurrent.Await +import scala.concurrent.duration._ +import scala.language.postfixOps + +/** + * Test case for the [[OAuth1InfoDAO]] class. + */ +class OAuth1InfoDAOSpec(implicit ev: ExecutionEnv) extends Specification with NoLanguageFeatures { + + "The `find` method" should { + "find an OAuth1 info for the given login info" in new Context { + Await.result(dao.save(loginInfo, authInfo), 10 seconds) + + dao.find(loginInfo) must beSome(authInfo).await + } + + "return None if no OAuth1 info for the given login info exists" in new Context { + dao.find(loginInfo.copy(providerKey = "new.key")) should beNone.await + } + } + + "The `add` method" should { + "add a new OAuth1 info" in new Context { + dao.add(loginInfo.copy(providerKey = "new.key"), authInfo) must beEqualTo(authInfo).await + dao.find(loginInfo.copy(providerKey = "new.key")) must beSome(authInfo).await + } + } + + "The `update` method" should { + "update an existing OAuth1 info" in new Context { + val updatedInfo = authInfo.copy(secret = "updated") + + dao.update(loginInfo, updatedInfo) must beEqualTo(updatedInfo).await + dao.find(loginInfo) must beSome(updatedInfo).await + } + } + + "The `save` method" should { + "insert a new OAuth1 info" in new Context { + dao.save(loginInfo.copy(providerKey = "new.key"), authInfo) must beEqualTo(authInfo).await + dao.find(loginInfo.copy(providerKey = "new.key")) must beSome(authInfo).await + } + + "update an existing OAuth1 info" in new Context { + val updatedInfo = authInfo.copy(secret = "updated") + + dao.update(loginInfo, updatedInfo) must beEqualTo(updatedInfo).await + dao.find(loginInfo) must beSome(updatedInfo).await + } + } + + "The `remove` method" should { + "remove an OAuth1 info" in new Context { + Await.result(dao.remove(loginInfo), 10 seconds) + dao.find(loginInfo) must beNone.await + } + } + + /** + * The context. + */ + trait Context extends Scope { + + /** + * The OAuth1 info DAO implementation. + */ + lazy val dao = new OAuth1InfoDAO + + /** + * A login info. + */ + lazy val loginInfo = LoginInfo("provider", "6253282") + + /** + * A OAuth1 info. + */ + lazy val authInfo = OAuth1Info("my.token", "my.consumer.secret") + } +} diff --git a/silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/OAuth2InfoDAOSpec.scala b/silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/OAuth2InfoDAOSpec.scala new file mode 100644 index 000000000..a1f682692 --- /dev/null +++ b/silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/OAuth2InfoDAOSpec.scala @@ -0,0 +1,112 @@ +/** + * 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 com.mohiva.play.silhouette.persistence.daos + +import com.mohiva.play.silhouette.api.LoginInfo +import com.mohiva.play.silhouette.impl.providers.OAuth2Info +import org.specs2.concurrent.ExecutionEnv +import org.specs2.control.NoLanguageFeatures +import org.specs2.mutable.Specification +import org.specs2.specification.Scope + +import scala.concurrent.Await +import scala.concurrent.duration._ +import scala.language.postfixOps + +/** + * Test case for the [[OAuth2InfoDAO]] class. + */ +class OAuth2InfoDAOSpec(implicit ev: ExecutionEnv) extends Specification with NoLanguageFeatures { + + "The `find` method" should { + "find an OAuth2 info for the given login info" in new Context { + Await.result(dao.save(loginInfo, authInfo), 10 seconds) + + dao.find(loginInfo) must beSome(authInfo).await + } + + "return None if no OAuth2 info for the given login info exists" in new Context { + dao.find(loginInfo.copy(providerKey = "new.key")) should beNone.await + } + } + + "The `add` method" should { + "add a new OAuth2 info" in new Context { + dao.add(loginInfo.copy(providerKey = "new.key"), authInfo) must beEqualTo(authInfo).await + dao.find(loginInfo.copy(providerKey = "new.key")) must beSome(authInfo).await + } + } + + "The `update` method" should { + "update an existing OAuth2 info" in new Context { + val updatedInfo = authInfo.copy(tokenType = Some("updated")) + + dao.update(loginInfo, updatedInfo) must beEqualTo(updatedInfo).await + dao.find(loginInfo) must beSome(updatedInfo).await + } + } + + "The `save` method" should { + "insert a new OAuth2 info" in new Context { + dao.save(loginInfo.copy(providerKey = "new.key"), authInfo) must beEqualTo(authInfo).await + dao.find(loginInfo.copy(providerKey = "new.key")) must beSome(authInfo).await + } + + "update an existing OAuth2 info" in new Context { + val updatedInfo = authInfo.copy(tokenType = Some("updated")) + + dao.update(loginInfo, updatedInfo) must beEqualTo(updatedInfo).await + dao.find(loginInfo) must beSome(updatedInfo).await + } + } + + "The `remove` method" should { + "remove an OAuth2 info" in new Context { + Await.result(dao.remove(loginInfo), 10 seconds) + dao.find(loginInfo) must beNone.await + } + } + + /** + * The context. + */ + trait Context extends Scope { + + /** + * The OAuth2 info DAO implementation. + */ + lazy val dao = new OAuth2InfoDAO + + /** + * A login info. + */ + lazy val loginInfo = LoginInfo("provider", "134405962728980") + + /** + * A OAuth2 info. + */ + lazy val authInfo = OAuth2Info( + accessToken = "my.access.token", + tokenType = Some("bearer"), + expiresIn = Some(5183836), + refreshToken = Some("my.refresh.token"), + params = Some(Map( + "param1" -> "value1", + "param2" -> "value2" + )) + ) + } +} diff --git a/silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/OpenIDInfoDAOSpec.scala b/silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/OpenIDInfoDAOSpec.scala new file mode 100644 index 000000000..3152f1fde --- /dev/null +++ b/silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/OpenIDInfoDAOSpec.scala @@ -0,0 +1,107 @@ +/** + * 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 com.mohiva.play.silhouette.persistence.daos + +import com.mohiva.play.silhouette.api.LoginInfo +import com.mohiva.play.silhouette.impl.providers.OpenIDInfo +import org.specs2.concurrent.ExecutionEnv +import org.specs2.control.NoLanguageFeatures +import org.specs2.mutable.Specification +import org.specs2.specification.Scope + +import scala.concurrent.Await +import scala.concurrent.duration._ +import scala.language.postfixOps + +/** + * Test case for the [[OpenIDInfoDAO]] class. + */ +class OpenIDInfoDAOSpec(implicit ev: ExecutionEnv) extends Specification with NoLanguageFeatures { + + "The `find` method" should { + "find an OAuth1 info for the given login info" in new Context { + Await.result(dao.save(loginInfo, authInfo), 10 seconds) + + dao.find(loginInfo) must beSome(authInfo).await + } + + "return None if no OAuth1 info for the given login info exists" in new Context { + dao.find(loginInfo.copy(providerKey = "new.key")) should beNone.await + } + } + + "The `add` method" should { + "add a new OAuth1 info" in new Context { + dao.add(loginInfo.copy(providerKey = "new.key"), authInfo) must beEqualTo(authInfo).await + dao.find(loginInfo.copy(providerKey = "new.key")) must beSome(authInfo).await + } + } + + "The `update` method" should { + "update an existing OAuth1 info" in new Context { + val updatedInfo = authInfo.copy(attributes = authInfo.attributes.updated("fullname", "updated")) + + dao.update(loginInfo, updatedInfo) must beEqualTo(updatedInfo).await + dao.find(loginInfo) must beSome(updatedInfo).await + } + } + + "The `save` method" should { + "insert a new OAuth1 info" in new Context { + dao.save(loginInfo.copy(providerKey = "new.key"), authInfo) must beEqualTo(authInfo).await + dao.find(loginInfo.copy(providerKey = "new.key")) must beSome(authInfo).await + } + + "update an existing OAuth1 info" in new Context { + val updatedInfo = authInfo.copy(attributes = authInfo.attributes.updated("fullname", "updated")) + + dao.update(loginInfo, updatedInfo) must beEqualTo(updatedInfo).await + dao.find(loginInfo) must beSome(updatedInfo).await + } + } + + "The `remove` method" should { + "remove an OAuth1 info" in new Context { + Await.result(dao.remove(loginInfo), 10 seconds) + dao.find(loginInfo) must beNone.await + } + } + + /** + * The context. + */ + trait Context extends Scope { + + /** + * The OAuth1 info DAO implementation. + */ + lazy val dao = new OpenIDInfoDAO + + /** + * A login info. + */ + lazy val loginInfo = LoginInfo("provider", "https://me.yahoo.com/a/Xs6hPjazdrMvmbn4jhQjkjkhcasdGdsKajq9we") + + /** + * A OAuth1 info. + */ + lazy val authInfo = OpenIDInfo("https://me.yahoo.com/a/Xs6hPjazdrMvmbn4jhQjkjkhcasdGdsKajq9we", Map( + "fullname" -> "Apollonia Vanova", + "email" -> "apollonia.vanova@watchmen.com", + "image" -> "https://s.yimg.com/dh/ap/social/profile/profile_b48.png" + )) + } +} diff --git a/silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/PasswordInfoDAOSpec.scala b/silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/PasswordInfoDAOSpec.scala new file mode 100644 index 000000000..a069383c8 --- /dev/null +++ b/silhouette-persistence-memory/src/test/scala/com/mohiva/play/silhouette/persistence/daos/PasswordInfoDAOSpec.scala @@ -0,0 +1,107 @@ +/** + * 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 com.mohiva.play.silhouette.persistence.daos + +import com.mohiva.play.silhouette.api.LoginInfo +import com.mohiva.play.silhouette.api.util.PasswordInfo +import com.mohiva.play.silhouette.impl.util.BCryptPasswordHasher +import org.specs2.concurrent.ExecutionEnv +import org.specs2.control.NoLanguageFeatures +import org.specs2.mutable.Specification +import org.specs2.specification.Scope + +import scala.concurrent.Await +import scala.concurrent.duration._ +import scala.language.postfixOps + +/** + * Test case for the [[PasswordInfoDAO]] class. + */ +class PasswordInfoDAOSpec(implicit ev: ExecutionEnv) extends Specification with NoLanguageFeatures { + + "The `find` method" should { + "find an password info for the given login info" in new Context { + Await.result(dao.save(loginInfo, authInfo), 10 seconds) + + dao.find(loginInfo) must beSome(authInfo).await + } + + "return None if no password info for the given login info exists" in new Context { + dao.find(loginInfo.copy(providerKey = "new.key")) should beNone.await + } + } + + "The `add` method" should { + "add a new password info" in new Context { + dao.add(loginInfo.copy(providerKey = "new.key"), authInfo) must beEqualTo(authInfo).await + dao.find(loginInfo.copy(providerKey = "new.key")) must beSome(authInfo).await + } + } + + "The `update` method" should { + "update an existing password info" in new Context { + val updatedInfo = authInfo.copy(password = "updated") + + dao.update(loginInfo, updatedInfo) must beEqualTo(updatedInfo).await + dao.find(loginInfo) must beSome(updatedInfo).await + } + } + + "The `save` method" should { + "insert a new password info" in new Context { + dao.save(loginInfo.copy(providerKey = "new.key"), authInfo) must beEqualTo(authInfo).await + dao.find(loginInfo.copy(providerKey = "new.key")) must beSome(authInfo).await + } + + "update an existing password info" in new Context { + val updatedInfo = authInfo.copy(password = "updated") + + dao.update(loginInfo, updatedInfo) must beEqualTo(updatedInfo).await + dao.find(loginInfo) must beSome(updatedInfo).await + } + } + + "The `remove` method" should { + "remove an password info" in new Context { + Await.result(dao.remove(loginInfo), 10 seconds) + dao.find(loginInfo) must beNone.await + } + } + + /** + * The context. + */ + trait Context extends Scope { + + /** + * The password info DAO implementation. + */ + lazy val dao = new PasswordInfoDAO + + /** + * A login info. + */ + lazy val loginInfo = LoginInfo("provider", "6253282") + + /** + * A password info. + */ + lazy val authInfo = PasswordInfo( + hasher = BCryptPasswordHasher.ID, + password = "$2a$10$bCBXbqjTaEcxXcjwc.kCXe.sI1b8.bTgV25gTD71KM00XdVd5MG6K" + ) + } +} diff --git a/silhouette-persistence/build.sbt b/silhouette-persistence/build.sbt new file mode 100644 index 000000000..d4361cd0e --- /dev/null +++ b/silhouette-persistence/build.sbt @@ -0,0 +1,10 @@ +import Dependencies._ + +libraryDependencies ++= Seq( + Library.Specs2.core % Test, + Library.Specs2.matcherExtra % Test, + Library.Specs2.mock % Test, + Library.scalaGuice % Test +) + +enablePlugins(Doc) diff --git a/silhouette/app/com/mohiva/play/silhouette/impl/daos/AuthInfoDAO.scala b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/AuthInfoDAO.scala similarity index 97% rename from silhouette/app/com/mohiva/play/silhouette/impl/daos/AuthInfoDAO.scala rename to silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/AuthInfoDAO.scala index 2ebf1afb9..0fc450b63 100644 --- a/silhouette/app/com/mohiva/play/silhouette/impl/daos/AuthInfoDAO.scala +++ b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/AuthInfoDAO.scala @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.mohiva.play.silhouette.impl.daos +package com.mohiva.play.silhouette.persistence.daos import com.mohiva.play.silhouette.api.{ AuthInfo, LoginInfo } diff --git a/silhouette/app/com/mohiva/play/silhouette/impl/daos/DelegableAuthInfoDAO.scala b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/DelegableAuthInfoDAO.scala similarity index 80% rename from silhouette/app/com/mohiva/play/silhouette/impl/daos/DelegableAuthInfoDAO.scala rename to silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/DelegableAuthInfoDAO.scala index 60a1c3675..8e6f883d2 100644 --- a/silhouette/app/com/mohiva/play/silhouette/impl/daos/DelegableAuthInfoDAO.scala +++ b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/DelegableAuthInfoDAO.scala @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.mohiva.play.silhouette.impl.daos +package com.mohiva.play.silhouette.persistence.daos import com.mohiva.play.silhouette.api.AuthInfo @@ -22,10 +22,10 @@ import scala.reflect.ClassTag /** * An implementation of the auth info DAO. * - * This abstract implementation of the [[com.mohiva.play.silhouette.impl.daos.AuthInfoDAO]] trait + * This abstract implementation of the [[com.mohiva.play.silhouette.persistence.daos.AuthInfoDAO]] trait * allows us to get the class tag of the auth info it is responsible for. Based on the class tag - * the [[com.mohiva.play.silhouette.impl.repositories.DelegableAuthInfoRepository]] class can - * delegate operations to the DAO which is responsible for the currently handled auth info. + * the [[com.mohiva.play.silhouette.persistence.repositories.DelegableAuthInfoRepository]] class + * can delegate operations to the DAO which is responsible for the currently handled auth info. * * @param classTag The class tag for the type parameter. * @tparam T The type of the auth info to store. diff --git a/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/InMemoryAuthInfoDAO.scala b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/InMemoryAuthInfoDAO.scala new file mode 100644 index 000000000..059f0f12c --- /dev/null +++ b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/InMemoryAuthInfoDAO.scala @@ -0,0 +1,97 @@ +/** + * 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 com.mohiva.play.silhouette.persistence.daos + +import com.mohiva.play.silhouette.api.{ AuthInfo, LoginInfo } + +import scala.collection.mutable +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future + +/** + * An implementation of the auth info DAO which stores the data in memory. + * + * This is not thread-safe implementation which should only be used for testing or development purpose. + */ +trait InMemoryAuthInfoDAO[T <: AuthInfo] extends DelegableAuthInfoDAO[T] { + + /** + * The data store for the auth info. + */ + var data: mutable.HashMap[LoginInfo, T] = mutable.HashMap() + + /** + * Finds the auth info which is linked with the specified login info. + * + * @param loginInfo The linked login info. + * @return The retrieved auth info or None if no auth info could be retrieved for the given login info. + */ + def find(loginInfo: LoginInfo): Future[Option[T]] = { + Future.successful(data.get(loginInfo)) + } + + /** + * Adds new auth info for the given login info. + * + * @param loginInfo The login info for which the auth info should be added. + * @param authInfo The auth info to add. + * @return The added auth info. + */ + def add(loginInfo: LoginInfo, authInfo: T): Future[T] = { + data += (loginInfo -> authInfo) + Future.successful(authInfo) + } + + /** + * Updates the auth info for the given login info. + * + * @param loginInfo The login info for which the auth info should be updated. + * @param authInfo The auth info to update. + * @return The updated auth info. + */ + def update(loginInfo: LoginInfo, authInfo: T): Future[T] = { + data += (loginInfo -> authInfo) + Future.successful(authInfo) + } + + /** + * Saves the auth info for the given login info. + * + * This method either adds the auth info if it doesn't exists or it updates the auth info + * if it already exists. + * + * @param loginInfo The login info for which the auth info should be saved. + * @param authInfo The auth info to save. + * @return The saved auth info. + */ + def save(loginInfo: LoginInfo, authInfo: T): Future[T] = { + find(loginInfo).flatMap { + case Some(_) => update(loginInfo, authInfo) + case None => add(loginInfo, authInfo) + } + } + + /** + * Removes the auth info for the given login info. + * + * @param loginInfo The login info for which the auth info should be removed. + * @return A future to wait for the process to be completed. + */ + def remove(loginInfo: LoginInfo): Future[Unit] = { + data -= loginInfo + Future.successful(()) + } +} diff --git a/silhouette/app/com/mohiva/play/silhouette/impl/daos/package.scala b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/package.scala similarity index 93% rename from silhouette/app/com/mohiva/play/silhouette/impl/daos/package.scala rename to silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/package.scala index 414d744fb..090514f09 100644 --- a/silhouette/app/com/mohiva/play/silhouette/impl/daos/package.scala +++ b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/daos/package.scala @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.mohiva.play.silhouette.impl +package com.mohiva.play.silhouette.persistence /** * Provides DAO implementations to persist and retrieve objects. diff --git a/silhouette/app/com/mohiva/play/silhouette/impl/daos/CacheAuthenticatorDAO.scala b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/repositories/CacheAuthenticatorRepository.scala similarity index 83% rename from silhouette/app/com/mohiva/play/silhouette/impl/daos/CacheAuthenticatorDAO.scala rename to silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/repositories/CacheAuthenticatorRepository.scala index be2b817cd..d0ea8e1ee 100644 --- a/silhouette/app/com/mohiva/play/silhouette/impl/daos/CacheAuthenticatorDAO.scala +++ b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/repositories/CacheAuthenticatorRepository.scala @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.mohiva.play.silhouette.impl.daos +package com.mohiva.play.silhouette.persistence.repositories import javax.inject.Inject import com.mohiva.play.silhouette.api.StorableAuthenticator +import com.mohiva.play.silhouette.api.repositories.AuthenticatorRepository import com.mohiva.play.silhouette.api.util.CacheLayer import scala.concurrent.Future @@ -25,13 +26,13 @@ import scala.concurrent.duration.Duration import scala.reflect.ClassTag /** - * Implementation of the authenticator DAO which uses the cache layer to persist the authenticator. + * Implementation of the authenticator repository which uses the cache layer to persist the authenticator. * * @param cacheLayer The cache layer implementation. * @tparam T The type of the authenticator to store. */ -class CacheAuthenticatorDAO[T <: StorableAuthenticator: ClassTag] @Inject() (cacheLayer: CacheLayer) - extends AuthenticatorDAO[T] { +class CacheAuthenticatorRepository[T <: StorableAuthenticator: ClassTag] @Inject() (cacheLayer: CacheLayer) + extends AuthenticatorRepository[T] { /** * Finds the authenticator for the given ID. diff --git a/silhouette/app/com/mohiva/play/silhouette/impl/repositories/DelegableAuthInfoRepository.scala b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/repositories/DelegableAuthInfoRepository.scala similarity index 79% rename from silhouette/app/com/mohiva/play/silhouette/impl/repositories/DelegableAuthInfoRepository.scala rename to silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/repositories/DelegableAuthInfoRepository.scala index 84ec1d39b..574f01866 100644 --- a/silhouette/app/com/mohiva/play/silhouette/impl/repositories/DelegableAuthInfoRepository.scala +++ b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/repositories/DelegableAuthInfoRepository.scala @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.mohiva.play.silhouette.impl.repositories +package com.mohiva.play.silhouette.persistence.repositories +import com.mohiva.play.silhouette.api.exceptions.ConfigurationException import com.mohiva.play.silhouette.api.repositories.AuthInfoRepository import com.mohiva.play.silhouette.api.{ AuthInfo, LoginInfo } -import com.mohiva.play.silhouette.impl.daos.{ AuthInfoDAO, DelegableAuthInfoDAO } -import com.mohiva.play.silhouette.impl.repositories.DelegableAuthInfoRepository._ +import com.mohiva.play.silhouette.persistence.daos.{ AuthInfoDAO, DelegableAuthInfoDAO } +import com.mohiva.play.silhouette.persistence.repositories.DelegableAuthInfoRepository._ import scala.concurrent.{ ExecutionContext, Future } import scala.reflect.ClassTag @@ -49,7 +50,7 @@ class DelegableAuthInfoRepository(daos: DelegableAuthInfoDAO[_]*)(implicit ec: E override def find[T <: AuthInfo](loginInfo: LoginInfo)(implicit tag: ClassTag[T]): Future[Option[T]] = { daos.find(_.classTag == tag) match { case Some(dao) => dao.find(loginInfo).map(_.map(_.asInstanceOf[T])) - case _ => throw new Exception(FindError.format(tag.runtimeClass)) + case _ => throw new ConfigurationException(FindError.format(tag.runtimeClass)) } } @@ -64,7 +65,7 @@ class DelegableAuthInfoRepository(daos: DelegableAuthInfoDAO[_]*)(implicit ec: E override def add[T <: AuthInfo](loginInfo: LoginInfo, authInfo: T): Future[T] = { daos.find(_.classTag.runtimeClass == authInfo.getClass) match { case Some(dao) => dao.asInstanceOf[AuthInfoDAO[T]].add(loginInfo, authInfo) - case _ => throw new Exception(AddError.format(authInfo.getClass)) + case _ => throw new ConfigurationException(AddError.format(authInfo.getClass)) } } @@ -79,7 +80,7 @@ class DelegableAuthInfoRepository(daos: DelegableAuthInfoDAO[_]*)(implicit ec: E override def update[T <: AuthInfo](loginInfo: LoginInfo, authInfo: T): Future[T] = { daos.find(_.classTag.runtimeClass == authInfo.getClass) match { case Some(dao) => dao.asInstanceOf[AuthInfoDAO[T]].update(loginInfo, authInfo) - case _ => throw new Exception(UpdateError.format(authInfo.getClass)) + case _ => throw new ConfigurationException(UpdateError.format(authInfo.getClass)) } } @@ -96,7 +97,7 @@ class DelegableAuthInfoRepository(daos: DelegableAuthInfoDAO[_]*)(implicit ec: E override def save[T <: AuthInfo](loginInfo: LoginInfo, authInfo: T): Future[T] = { daos.find(_.classTag.runtimeClass == authInfo.getClass) match { case Some(dao) => dao.asInstanceOf[AuthInfoDAO[T]].save(loginInfo, authInfo) - case _ => throw new Exception(SaveError.format(authInfo.getClass)) + case _ => throw new ConfigurationException(SaveError.format(authInfo.getClass)) } } @@ -111,7 +112,7 @@ class DelegableAuthInfoRepository(daos: DelegableAuthInfoDAO[_]*)(implicit ec: E override def remove[T <: AuthInfo](loginInfo: LoginInfo)(implicit tag: ClassTag[T]): Future[Unit] = { daos.find(_.classTag == tag) match { case Some(dao) => dao.remove(loginInfo) - case _ => throw new Exception(RemoveError.format(tag.runtimeClass)) + case _ => throw new ConfigurationException(RemoveError.format(tag.runtimeClass)) } } } @@ -124,9 +125,9 @@ object DelegableAuthInfoRepository { /** * The error messages. */ - val AddError = "Cannot add auth info of type: %s" - val FindError = "Cannot find auth info of type: %s" - val UpdateError = "Cannot update auth info of type: %s" - val SaveError = "Cannot save auth info of type: %s" - val RemoveError = "Cannot remove auth info of type: %s" + val AddError = "Cannot add auth info of type: %s; Please configure the DAO for this type" + val FindError = "Cannot find auth info of type: %s; Please configure the DAO for this type" + val UpdateError = "Cannot update auth info of type: %s; Please configure the DAO for this type" + val SaveError = "Cannot save auth info of type: %s; Please configure the DAO for this type" + val RemoveError = "Cannot remove auth info of type: %s; Please configure the DAO for this type" } diff --git a/silhouette/app/com/mohiva/play/silhouette/impl/repositories/package.scala b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/repositories/package.scala similarity index 93% rename from silhouette/app/com/mohiva/play/silhouette/impl/repositories/package.scala rename to silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/repositories/package.scala index d563239b3..e51e8dc3a 100644 --- a/silhouette/app/com/mohiva/play/silhouette/impl/repositories/package.scala +++ b/silhouette-persistence/src/main/scala/com/mohiva/play/silhouette/persistence/repositories/package.scala @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.mohiva.play.silhouette.impl +package com.mohiva.play.silhouette.persistence /** * Provides implementations of the repositories. diff --git a/silhouette-persistence/src/test/scala/com/mohiva/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala b/silhouette-persistence/src/test/scala/com/mohiva/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala new file mode 100644 index 000000000..49f68491f --- /dev/null +++ b/silhouette-persistence/src/test/scala/com/mohiva/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala @@ -0,0 +1,107 @@ +/** + * 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 com.mohiva.play.silhouette.persistence.daos + +import com.mohiva.play.silhouette.api.{ AuthInfo, LoginInfo } +import org.specs2.concurrent.ExecutionEnv +import org.specs2.control.NoLanguageFeatures +import org.specs2.mutable.Specification +import org.specs2.specification.Scope + +import scala.concurrent.Await +import scala.concurrent.duration._ +import scala.language.postfixOps + +/** + * Test case for the [[InMemoryAuthInfoDAO]] trait. + */ +class InMemoryAuthInfoDAOSpec(implicit ev: ExecutionEnv) extends Specification with NoLanguageFeatures { + + "The `find` method" should { + "find an OAuth1 info for the given login info" in new Context { + Await.result(dao.save(loginInfo, authInfo), 10 seconds) + + dao.find(loginInfo) must beSome(authInfo).await + } + + "return None if no OAuth1 info for the given login info exists" in new Context { + dao.find(loginInfo.copy(providerKey = "new.key")) should beNone.await + } + } + + "The `add` method" should { + "add a new OAuth1 info" in new Context { + dao.add(loginInfo.copy(providerKey = "new.key"), authInfo) must beEqualTo(authInfo).await + dao.find(loginInfo.copy(providerKey = "new.key")) must beSome(authInfo).await + } + } + + "The `update` method" should { + "update an existing OAuth1 info" in new Context { + val updatedInfo = authInfo.copy(data = "updated") + + dao.update(loginInfo, updatedInfo) must beEqualTo(updatedInfo).await + dao.find(loginInfo) must beSome(updatedInfo).await + } + } + + "The `save` method" should { + "insert a new OAuth1 info" in new Context { + dao.save(loginInfo.copy(providerKey = "new.key"), authInfo) must beEqualTo(authInfo).await + dao.find(loginInfo.copy(providerKey = "new.key")) must beSome(authInfo).await + } + + "update an existing OAuth1 info" in new Context { + val updatedInfo = authInfo.copy(data = "updated") + + dao.update(loginInfo, updatedInfo) must beEqualTo(updatedInfo).await + dao.find(loginInfo) must beSome(updatedInfo).await + } + } + + "The `remove` method" should { + "remove an OAuth1 info" in new Context { + Await.result(dao.remove(loginInfo), 10 seconds) + dao.find(loginInfo) must beNone.await + } + } + + /** + * The context. + */ + trait Context extends Scope { + + /** + * A test auth info implementation. + */ + case class TestInfo(data: String) extends AuthInfo + + /** + * The OAuth1 info DAO implementation. + */ + lazy val dao = new InMemoryAuthInfoDAO[TestInfo] {} + + /** + * A login info. + */ + lazy val loginInfo = LoginInfo("provider", "6253282") + + /** + * A OAuth1 info. + */ + lazy val authInfo = TestInfo("my.data") + } +} diff --git a/silhouette/test/com/mohiva/play/silhouette/impl/daos/CacheAuthenticatorDAOSpec.scala b/silhouette-persistence/src/test/scala/com/mohiva/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala similarity index 76% rename from silhouette/test/com/mohiva/play/silhouette/impl/daos/CacheAuthenticatorDAOSpec.scala rename to silhouette-persistence/src/test/scala/com/mohiva/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala index 1f2409328..a1b00b9fc 100644 --- a/silhouette/test/com/mohiva/play/silhouette/impl/daos/CacheAuthenticatorDAOSpec.scala +++ b/silhouette-persistence/src/test/scala/com/mohiva/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala @@ -13,34 +13,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.mohiva.play.silhouette.impl.daos +package com.mohiva.play.silhouette.persistence.repositories import com.mohiva.play.silhouette.api.StorableAuthenticator import com.mohiva.play.silhouette.api.util.CacheLayer +import org.specs2.concurrent.ExecutionEnv import org.specs2.mock.Mockito +import org.specs2.mutable.Specification import org.specs2.specification.Scope -import play.api.test.PlaySpecification import scala.concurrent.Future import scala.concurrent.duration.Duration /** - * Test case for the [[com.mohiva.play.silhouette.impl.daos.CacheAuthenticatorDAO]] class. + * Test case for the [[CacheAuthenticatorRepository]] class. */ -class CacheAuthenticatorDAOSpec extends PlaySpecification with Mockito { +class CacheAuthenticatorRepositorySpec(implicit ev: ExecutionEnv) extends Specification with Mockito { "The `find` method" should { "return value from cache" in new Context { cacheLayer.find[StorableAuthenticator]("test-id") returns Future.successful(Some(authenticator)) - await(dao.find("test-id")) must beSome(authenticator) + repository.find("test-id") must beSome(authenticator).await there was one(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) - await(dao.find("test-id")) must beNone + repository.find("test-id") must beNone.await there was one(cacheLayer).find[StorableAuthenticator]("test-id") } } @@ -50,7 +51,7 @@ class CacheAuthenticatorDAOSpec extends PlaySpecification with Mockito { authenticator.id returns "test-id" cacheLayer.save("test-id", authenticator, Duration.Inf) returns Future.successful(authenticator) - await(dao.add(authenticator)) must be equalTo authenticator + repository.add(authenticator) must beEqualTo(authenticator).await there was one(cacheLayer).save("test-id", authenticator, Duration.Inf) } } @@ -60,7 +61,7 @@ class CacheAuthenticatorDAOSpec extends PlaySpecification with Mockito { authenticator.id returns "test-id" cacheLayer.save("test-id", authenticator, Duration.Inf) returns Future.successful(authenticator) - await(dao.update(authenticator)) must be equalTo authenticator + repository.update(authenticator) must beEqualTo(authenticator).await there was one(cacheLayer).save("test-id", authenticator, Duration.Inf) } } @@ -69,7 +70,7 @@ class CacheAuthenticatorDAOSpec extends PlaySpecification with Mockito { "remove value from cache" in new Context { cacheLayer.remove("test-id") returns Future.successful(()) - await(dao.remove("test-id")) must be equalTo (()) + repository.remove("test-id") must beEqualTo(()).await there was one(cacheLayer).remove("test-id") } } @@ -90,8 +91,8 @@ class CacheAuthenticatorDAOSpec extends PlaySpecification with Mockito { lazy val cacheLayer = mock[CacheLayer] /** - * The dAO to test. + * The repository to test. */ - lazy val dao = new CacheAuthenticatorDAO[StorableAuthenticator](cacheLayer) + lazy val repository = new CacheAuthenticatorRepository[StorableAuthenticator](cacheLayer) } } diff --git a/silhouette/test/com/mohiva/play/silhouette/impl/repositories/DelegableAuthInfoRepositorySpec.scala b/silhouette-persistence/src/test/scala/com/mohiva/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala similarity index 56% rename from silhouette/test/com/mohiva/play/silhouette/impl/repositories/DelegableAuthInfoRepositorySpec.scala rename to silhouette-persistence/src/test/scala/com/mohiva/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala index 3371bb6a7..79bd6237c 100644 --- a/silhouette/test/com/mohiva/play/silhouette/impl/repositories/DelegableAuthInfoRepositorySpec.scala +++ b/silhouette-persistence/src/test/scala/com/mohiva/play/silhouette/persistence/repositories/DelegableAuthInfoRepositorySpec.scala @@ -13,64 +13,67 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.mohiva.play.silhouette.impl.repositories +package com.mohiva.play.silhouette.persistence.repositories import com.google.inject.{ AbstractModule, Guice, Provides } +import com.mohiva.play.silhouette.api.exceptions.ConfigurationException import com.mohiva.play.silhouette.api.repositories.AuthInfoRepository import com.mohiva.play.silhouette.api.util.PasswordInfo import com.mohiva.play.silhouette.api.{ AuthInfo, LoginInfo } -import com.mohiva.play.silhouette.impl.daos.DelegableAuthInfoDAO import com.mohiva.play.silhouette.impl.providers.{ OAuth1Info, OAuth2Info } -import com.mohiva.play.silhouette.impl.repositories.DelegableAuthInfoRepository._ +import com.mohiva.play.silhouette.persistence.daos.{ DelegableAuthInfoDAO, InMemoryAuthInfoDAO } +import com.mohiva.play.silhouette.persistence.repositories.DelegableAuthInfoRepository._ 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 play.api.test.PlaySpecification -import play.api.libs.concurrent.Execution.Implicits._ -import scala.collection.mutable -import scala.concurrent.Future -import scala.reflect.ClassTag +import scala.concurrent.Await +import scala.concurrent.duration._ +import scala.language.postfixOps /** * Test case for the [[DelegableAuthInfoRepository]] trait. */ -class DelegableAuthInfoRepositorySpec extends PlaySpecification with Mockito { +class DelegableAuthInfoRepositorySpec(implicit ev: ExecutionEnv) + extends Specification with Mockito with NoLanguageFeatures { "The `find` method" should { "delegate the PasswordInfo to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(passwordInfoDAO.add(loginInfo, passwordInfo)) + Await.result(passwordInfoDAO.add(loginInfo, passwordInfo), 10 seconds) - await(service.find[PasswordInfo](loginInfo)) must beSome(passwordInfo) + service.find[PasswordInfo](loginInfo) must beSome(passwordInfo).await there was one(passwordInfoDAO).find(loginInfo) } "delegate the OAuth1Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(oauth1InfoDAO.add(loginInfo, oauth1Info)) + Await.result(oauth1InfoDAO.add(loginInfo, oauth1Info), 10 seconds) - await(service.find[OAuth1Info](loginInfo)) must beSome(oauth1Info) + service.find[OAuth1Info](loginInfo) must beSome(oauth1Info).await there was one(oauth1InfoDAO).find(loginInfo) } "delegate the OAuth2Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(oauth2InfoDAO.add(loginInfo, oauth2Info)) + Await.result(oauth2InfoDAO.add(loginInfo, oauth2Info), 10 seconds) - await(service.find[OAuth2Info](loginInfo)) must beSome(oauth2Info) + service.find[OAuth2Info](loginInfo) must beSome(oauth2Info).await there was one(oauth2InfoDAO).find(loginInfo) } - "throw an Exception if an unsupported type was given" in new Context { + "throw a ConfigurationException if an unsupported type was given" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.find[UnsupportedInfo](loginInfo)) must throwA[Exception].like { - case e => e.getMessage must startWith(FindError.format("")) - } + service.find[UnsupportedInfo](loginInfo) must throwA[ConfigurationException].like { + case e => e.getMessage must startWith(FindError.format(classOf[UnsupportedInfo])) + }.await } } @@ -78,30 +81,30 @@ class DelegableAuthInfoRepositorySpec extends PlaySpecification with Mockito { "delegate the PasswordInfo to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.add(loginInfo, passwordInfo)) must be equalTo passwordInfo + service.add(loginInfo, passwordInfo) must beEqualTo(passwordInfo).await there was one(passwordInfoDAO).add(loginInfo, passwordInfo) } "delegate the OAuth1Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.add(loginInfo, oauth1Info)) must be equalTo oauth1Info + service.add(loginInfo, oauth1Info) must beEqualTo(oauth1Info).await there was one(oauth1InfoDAO).add(loginInfo, oauth1Info) } "delegate the OAuth2Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.add(loginInfo, oauth2Info)) must be equalTo oauth2Info + service.add(loginInfo, oauth2Info) must beEqualTo(oauth2Info).await there was one(oauth2InfoDAO).add(loginInfo, oauth2Info) } - "throw an Exception if an unsupported type was given" in new Context { + "throw a ConfigurationException if an unsupported type was given" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.add(loginInfo, new UnsupportedInfo)) must throwA[Exception].like { - case e => e.getMessage must startWith(AddError.format("")) - } + service.add(loginInfo, new UnsupportedInfo) must throwA[ConfigurationException].like { + case e => e.getMessage must startWith(AddError.format(classOf[UnsupportedInfo])) + }.await } } @@ -109,30 +112,30 @@ class DelegableAuthInfoRepositorySpec extends PlaySpecification with Mockito { "delegate the PasswordInfo to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.update(loginInfo, passwordInfo)) must be equalTo passwordInfo + service.update(loginInfo, passwordInfo) must beEqualTo(passwordInfo).await there was one(passwordInfoDAO).update(loginInfo, passwordInfo) } "delegate the OAuth1Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.update(loginInfo, oauth1Info)) must be equalTo oauth1Info + service.update(loginInfo, oauth1Info) must beEqualTo(oauth1Info).await there was one(oauth1InfoDAO).update(loginInfo, oauth1Info) } "delegate the OAuth2Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.update(loginInfo, oauth2Info)) must be equalTo oauth2Info + service.update(loginInfo, oauth2Info) must beEqualTo(oauth2Info).await there was one(oauth2InfoDAO).update(loginInfo, oauth2Info) } - "throw an Exception if an unsupported type was given" in new Context { + "throw a ConfigurationException if an unsupported type was given" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.update(loginInfo, new UnsupportedInfo)) must throwA[Exception].like { - case e => e.getMessage must startWith(UpdateError.format("")) - } + service.update(loginInfo, new UnsupportedInfo) must throwA[ConfigurationException].like { + case e => e.getMessage must startWith(UpdateError.format(classOf[UnsupportedInfo])) + }.await } } @@ -140,30 +143,30 @@ class DelegableAuthInfoRepositorySpec extends PlaySpecification with Mockito { "delegate the PasswordInfo to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.save(loginInfo, passwordInfo)) must be equalTo passwordInfo + service.save(loginInfo, passwordInfo) must beEqualTo(passwordInfo).await there was one(passwordInfoDAO).save(loginInfo, passwordInfo) } "delegate the OAuth1Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.save(loginInfo, oauth1Info)) must be equalTo oauth1Info + service.save(loginInfo, oauth1Info) must beEqualTo(oauth1Info).await there was one(oauth1InfoDAO).save(loginInfo, oauth1Info) } "delegate the OAuth2Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.save(loginInfo, oauth2Info)) must be equalTo oauth2Info + service.save(loginInfo, oauth2Info) must beEqualTo(oauth2Info).await there was one(oauth2InfoDAO).save(loginInfo, oauth2Info) } - "throw an Exception if an unsupported type was given" in new Context { + "throw a ConfigurationException if an unsupported type was given" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.save(loginInfo, new UnsupportedInfo)) must throwA[Exception].like { - case e => e.getMessage must startWith(SaveError.format("")) - } + service.save(loginInfo, new UnsupportedInfo) must throwA[ConfigurationException].like { + case e => e.getMessage must startWith(SaveError.format(classOf[UnsupportedInfo])) + }.await } } @@ -171,36 +174,36 @@ class DelegableAuthInfoRepositorySpec extends PlaySpecification with Mockito { "delegate the PasswordInfo to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(passwordInfoDAO.add(loginInfo, passwordInfo)) + Await.result(passwordInfoDAO.add(loginInfo, passwordInfo), 10 seconds) - await(service.remove[PasswordInfo](loginInfo)) must be equalTo (()) + service.remove[PasswordInfo](loginInfo) must beEqualTo(()).await there was one(passwordInfoDAO).remove(loginInfo) } "delegate the OAuth1Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(oauth1InfoDAO.add(loginInfo, oauth1Info)) + Await.result(oauth1InfoDAO.add(loginInfo, oauth1Info), 10 seconds) - await(service.remove[OAuth1Info](loginInfo)) must be equalTo (()) + service.remove[OAuth1Info](loginInfo) must beEqualTo(()).await there was one(oauth1InfoDAO).remove(loginInfo) } "delegate the OAuth2Info to the correct DAO" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(oauth2InfoDAO.add(loginInfo, oauth2Info)) + Await.result(oauth2InfoDAO.add(loginInfo, oauth2Info), 10 seconds) - await(service.remove[OAuth2Info](loginInfo)) must be equalTo (()) + service.remove[OAuth2Info](loginInfo) must beEqualTo(()).await there was one(oauth2InfoDAO).remove(loginInfo) } - "throw an Exception if an unsupported type was given" in new Context { + "throw a ConfigurationException if an unsupported type was given" in new Context { val loginInfo = LoginInfo("credentials", "1") - await(service.remove[UnsupportedInfo](loginInfo)) must throwA[Exception].like { - case e => e.getMessage must startWith(RemoveError.format("")) - } + service.remove[UnsupportedInfo](loginInfo) must throwA[ConfigurationException].like { + case e => e.getMessage must startWith(RemoveError.format(classOf[UnsupportedInfo])) + }.await } } @@ -241,7 +244,7 @@ class DelegableAuthInfoRepositorySpec extends PlaySpecification with Mockito { /** * The Guice module. * - * This is to test if the [[com.mohiva.play.silhouette.impl.daos.DelegableAuthInfoDAO]] can be used for + * This is to test if the [[com.mohiva.play.silhouette.persistence.daos.DelegableAuthInfoDAO]] can be used for * dependency injection because it depends on an implicit [[scala.reflect.ClassTag]] parameter which the * compiler injects at compile time. */ @@ -288,78 +291,5 @@ class DelegableAuthInfoRepositorySpec extends PlaySpecification with Mockito { * The DAO to store the OAuth2 information. */ class OAuth2InfoDAO extends InMemoryAuthInfoDAO[OAuth2Info] - - /** - * An abstract in-memory test helper. - */ - abstract class InMemoryAuthInfoDAO[T <: AuthInfo: ClassTag] extends DelegableAuthInfoDAO[T] { - - /** - * The data store for the auth info. - */ - var data: mutable.HashMap[LoginInfo, T] = mutable.HashMap() - - /** - * Finds the auth info which is linked with the specified login info. - * - * @param loginInfo The linked login info. - * @return The retrieved auth info or None if no auth info could be retrieved for the given login info. - */ - def find(loginInfo: LoginInfo): Future[Option[T]] = { - Future.successful(data.get(loginInfo)) - } - - /** - * Adds new auth info for the given login info. - * - * @param loginInfo The login info for which the auth info should be added. - * @param authInfo The auth info to add. - * @return The added auth info. - */ - def add(loginInfo: LoginInfo, authInfo: T): Future[T] = { - data += (loginInfo -> authInfo) - Future.successful(authInfo) - } - - /** - * Updates the auth info for the given login info. - * - * @param loginInfo The login info for which the auth info should be updated. - * @param authInfo The auth info to update. - * @return The updated auth info. - */ - def update(loginInfo: LoginInfo, authInfo: T): Future[T] = { - data += (loginInfo -> authInfo) - Future.successful(authInfo) - } - - /** - * Saves the auth info for the given login info. - * - * This method either adds the auth info if it doesn't exists or it updates the auth info - * if it already exists. - * - * @param loginInfo The login info for which the auth info should be saved. - * @param authInfo The auth info to save. - * @return The saved auth info. - */ - def save(loginInfo: LoginInfo, authInfo: T): Future[T] = { - find(loginInfo).flatMap { - case Some(_) => update(loginInfo, authInfo) - case None => add(loginInfo, authInfo) - } - } - - /** - * Removes the auth info for the given login info. - * - * @param loginInfo The login info for which the auth info should be removed. - * @return A future to wait for the process to be completed. - */ - def remove(loginInfo: LoginInfo): Future[Unit] = { - data -= loginInfo - Future.successful(()) - } - } } } diff --git a/silhouette-testkit/app/com/mohiva/play/silhouette/test/Fakes.scala b/silhouette-testkit/app/com/mohiva/play/silhouette/test/Fakes.scala index 84e59debf..47f03ebeb 100644 --- a/silhouette-testkit/app/com/mohiva/play/silhouette/test/Fakes.scala +++ b/silhouette-testkit/app/com/mohiva/play/silhouette/test/Fakes.scala @@ -18,10 +18,10 @@ package com.mohiva.play.silhouette.test import java.util.UUID import com.mohiva.play.silhouette.api._ +import com.mohiva.play.silhouette.api.repositories.AuthenticatorRepository import com.mohiva.play.silhouette.api.services.{ AuthenticatorService, IdentityService } import com.mohiva.play.silhouette.api.util.Clock import com.mohiva.play.silhouette.impl.authenticators._ -import com.mohiva.play.silhouette.impl.daos.AuthenticatorDAO import com.mohiva.play.silhouette.impl.util.{ DefaultFingerprintGenerator, SecureRandomIDGenerator } import play.api.libs.concurrent.Execution.Implicits._ import play.api.mvc.RequestHeader @@ -57,11 +57,11 @@ class FakeIdentityService[I <: Identity](identities: (LoginInfo, I)*) extends Id } /** - * A fake authenticator DAO which stores authenticators in memory. + * A fake authenticator repository which persists authenticators in memory. * * @tparam T The type of the authenticator to handle. */ -class FakeAuthenticatorDAO[T <: StorableAuthenticator] extends AuthenticatorDAO[T] { +class FakeAuthenticatorRepository[T <: StorableAuthenticator] extends AuthenticatorRepository[T] { /** * The data store for the OAuth1 info. @@ -135,7 +135,7 @@ case class FakeCookieAuthenticatorService() extends CookieAuthenticatorService( */ case class FakeBearerTokenAuthenticatorService() extends BearerTokenAuthenticatorService( new BearerTokenAuthenticatorSettings(), - new FakeAuthenticatorDAO[BearerTokenAuthenticator], + new FakeAuthenticatorRepository[BearerTokenAuthenticator], new SecureRandomIDGenerator(), Clock()) diff --git a/silhouette-testkit/build.sbt b/silhouette-testkit/build.sbt index f02906128..d73d110f8 100644 --- a/silhouette-testkit/build.sbt +++ b/silhouette-testkit/build.sbt @@ -3,8 +3,8 @@ import Dependencies._ libraryDependencies ++= Seq( Library.Play.test, Library.Play.specs2 % Test, - Library.Specs2.matcherExtra % Test, - Library.mockito % Test, + Library.Play.Specs2.matcherExtra % Test, + Library.Play.Specs2.mock % Test, Library.scalaGuice % Test, Library.akkaTestkit % Test ) diff --git a/silhouette-testkit/test/com/mohiva/play/silhouette/test/FakesSpec.scala b/silhouette-testkit/test/com/mohiva/play/silhouette/test/FakesSpec.scala index 55080b6a4..74985dffa 100644 --- a/silhouette-testkit/test/com/mohiva/play/silhouette/test/FakesSpec.scala +++ b/silhouette-testkit/test/com/mohiva/play/silhouette/test/FakesSpec.scala @@ -52,11 +52,11 @@ class FakesSpec extends PlaySpecification with JsonMatchers { } } - "The `find` method of the `FakeAuthenticatorDAO`" should { + "The `find` method of the `FakeAuthenticatorRepository`" should { "return an authenticator for the given ID" in { val loginInfo = LoginInfo("test", "test") val authenticator = new FakeAuthenticator(loginInfo, "test") - val dao = new FakeAuthenticatorDAO[FakeAuthenticator]() + val dao = new FakeAuthenticatorRepository[FakeAuthenticator]() await(dao.add(authenticator)) @@ -64,37 +64,37 @@ class FakesSpec extends PlaySpecification with JsonMatchers { } "return None if no authenticator could be found for the given ID" in { - val dao = new FakeAuthenticatorDAO[FakeAuthenticator]() + val dao = new FakeAuthenticatorRepository[FakeAuthenticator]() await(dao.find("test")) must beNone } } - "The `add` method of the `FakeAuthenticatorDAO`" should { + "The `add` method of the `FakeAuthenticatorRepository`" should { "add an authenticator" in { val loginInfo = LoginInfo("test", "test") val authenticator = new FakeAuthenticator(loginInfo) - val dao = new FakeAuthenticatorDAO[FakeAuthenticator]() + val dao = new FakeAuthenticatorRepository[FakeAuthenticator]() await(dao.add(authenticator)) must be equalTo authenticator } } - "The `update` method of the `FakeAuthenticatorDAO`" should { + "The `update` method of the `FakeAuthenticatorRepository`" should { "update an authenticator" in { val loginInfo = LoginInfo("test", "test") val authenticator = new FakeAuthenticator(loginInfo) - val dao = new FakeAuthenticatorDAO[FakeAuthenticator]() + val dao = new FakeAuthenticatorRepository[FakeAuthenticator]() await(dao.update(authenticator)) must be equalTo authenticator } } - "The `remove` method of the `FakeAuthenticatorDAO`" should { + "The `remove` method of the `FakeAuthenticatorRepository`" should { "remove an authenticator" in { val loginInfo = LoginInfo("test", "test") val authenticator = new FakeAuthenticator(loginInfo, "test") - val dao = new FakeAuthenticatorDAO[FakeAuthenticator]() + val dao = new FakeAuthenticatorRepository[FakeAuthenticator]() await(dao.add(authenticator)) await(dao.find("test")) must beSome(authenticator) diff --git a/silhouette/app/com/mohiva/play/silhouette/impl/daos/AuthenticatorDAO.scala b/silhouette/app/com/mohiva/play/silhouette/api/repositories/AuthenticatorRepository.scala similarity index 88% rename from silhouette/app/com/mohiva/play/silhouette/impl/daos/AuthenticatorDAO.scala rename to silhouette/app/com/mohiva/play/silhouette/api/repositories/AuthenticatorRepository.scala index 1be9069c9..02957b61a 100644 --- a/silhouette/app/com/mohiva/play/silhouette/impl/daos/AuthenticatorDAO.scala +++ b/silhouette/app/com/mohiva/play/silhouette/api/repositories/AuthenticatorRepository.scala @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.mohiva.play.silhouette.impl.daos +package com.mohiva.play.silhouette.api.repositories import com.mohiva.play.silhouette.api.StorableAuthenticator import scala.concurrent.Future /** - * The DAO to persist the authenticator. + * A trait that provides the means to persist authenticator information for the Silhouette module. * * @tparam T The type of the authenticator to store. */ -trait AuthenticatorDAO[T <: StorableAuthenticator] { +trait AuthenticatorRepository[T <: StorableAuthenticator] { /** * Finds the authenticator for the given ID. diff --git a/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/BearerTokenAuthenticator.scala b/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/BearerTokenAuthenticator.scala index ded2d07a5..f2b3ea526 100644 --- a/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/BearerTokenAuthenticator.scala +++ b/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/BearerTokenAuthenticator.scala @@ -18,12 +18,12 @@ package com.mohiva.play.silhouette.impl.authenticators import com.mohiva.play.silhouette._ import com.mohiva.play.silhouette.api.Authenticator.Implicits._ import com.mohiva.play.silhouette.api.exceptions._ +import com.mohiva.play.silhouette.api.repositories.AuthenticatorRepository import com.mohiva.play.silhouette.api.services.AuthenticatorService._ import com.mohiva.play.silhouette.api.services.{ AuthenticatorResult, AuthenticatorService } import com.mohiva.play.silhouette.api.util.{ Clock, IDGenerator } import com.mohiva.play.silhouette.api.{ ExpirableAuthenticator, Logger, LoginInfo, StorableAuthenticator } import com.mohiva.play.silhouette.impl.authenticators.BearerTokenAuthenticatorService._ -import com.mohiva.play.silhouette.impl.daos.AuthenticatorDAO import org.joda.time.DateTime import play.api.mvc.{ RequestHeader, Result } @@ -67,14 +67,14 @@ case class BearerTokenAuthenticator( * The service that handles the bearer token authenticator. * * @param settings The authenticator settings. - * @param dao The DAO to store the authenticator. + * @param repository The repository to persist the authenticator. * @param idGenerator The ID generator used to create the authenticator ID. * @param clock The clock implementation. * @param executionContext The execution context to handle the asynchronous operations. */ class BearerTokenAuthenticatorService( settings: BearerTokenAuthenticatorSettings, - dao: AuthenticatorDAO[BearerTokenAuthenticator], + repository: AuthenticatorRepository[BearerTokenAuthenticator], idGenerator: IDGenerator, clock: Clock)(implicit val executionContext: ExecutionContext) extends AuthenticatorService[BearerTokenAuthenticator] @@ -109,7 +109,7 @@ class BearerTokenAuthenticatorService( */ override def retrieve(implicit request: RequestHeader): Future[Option[BearerTokenAuthenticator]] = { Future.from(Try(request.headers.get(settings.headerName))).flatMap { - case Some(token) => dao.find(token) + case Some(token) => repository.find(token) case None => Future.successful(None) }.recover { case e => throw new AuthenticatorRetrievalException(RetrieveError.format(ID), e) @@ -125,7 +125,7 @@ class BearerTokenAuthenticatorService( * @return The serialized authenticator value. */ override def init(authenticator: BearerTokenAuthenticator)(implicit request: RequestHeader): Future[String] = { - dao.add(authenticator).map { a => + repository.add(authenticator).map { a => a.id }.recover { case e => throw new AuthenticatorInitializationException(InitError.format(ID, authenticator), e) @@ -184,7 +184,7 @@ class BearerTokenAuthenticatorService( override def update(authenticator: BearerTokenAuthenticator, result: Result)( implicit request: RequestHeader): Future[AuthenticatorResult] = { - dao.update(authenticator).map { a => + repository.update(authenticator).map { a => AuthenticatorResult(result) }.recover { case e => throw new AuthenticatorUpdateException(UpdateError.format(ID, authenticator), e) @@ -205,7 +205,7 @@ class BearerTokenAuthenticatorService( override def renew(authenticator: BearerTokenAuthenticator)( implicit request: RequestHeader): Future[String] = { - dao.remove(authenticator.id).flatMap { _ => + repository.remove(authenticator.id).flatMap { _ => create(authenticator.loginInfo).flatMap(init) }.recover { case e => throw new AuthenticatorRenewalException(RenewError.format(ID, authenticator), e) @@ -241,7 +241,7 @@ class BearerTokenAuthenticatorService( override def discard(authenticator: BearerTokenAuthenticator, result: Result)( implicit request: RequestHeader): Future[AuthenticatorResult] = { - dao.remove(authenticator.id).map { _ => + repository.remove(authenticator.id).map { _ => AuthenticatorResult(result) }.recover { case e => throw new AuthenticatorDiscardingException(DiscardError.format(ID, authenticator), e) diff --git a/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/CookieAuthenticator.scala b/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/CookieAuthenticator.scala index 6306c907b..172abe3e9 100644 --- a/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/CookieAuthenticator.scala +++ b/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/CookieAuthenticator.scala @@ -22,13 +22,13 @@ package com.mohiva.play.silhouette.impl.authenticators import com.mohiva.play.silhouette._ import com.mohiva.play.silhouette.api.Authenticator.Implicits._ import com.mohiva.play.silhouette.api.exceptions._ +import com.mohiva.play.silhouette.api.repositories.AuthenticatorRepository import com.mohiva.play.silhouette.api.services.AuthenticatorService._ import com.mohiva.play.silhouette.api.services.{ AuthenticatorResult, AuthenticatorService } import com.mohiva.play.silhouette.api.util._ import com.mohiva.play.silhouette.api.util.JsonFormats._ import com.mohiva.play.silhouette.api.{ ExpirableAuthenticator, Logger, LoginInfo, StorableAuthenticator } import com.mohiva.play.silhouette.impl.authenticators.CookieAuthenticatorService._ -import com.mohiva.play.silhouette.impl.daos.AuthenticatorDAO import org.joda.time.DateTime import play.api.http.HeaderNames import play.api.libs.Crypto @@ -139,7 +139,7 @@ object CookieAuthenticator extends Logger { * The service that handles the cookie authenticator. * * @param settings The cookie settings. - * @param dao The DAO to store the authenticator. Set it to None to use a stateless approach. + * @param repository The repository to persist the authenticator. Set it to None to use a stateless approach. * @param fingerprintGenerator The fingerprint generator implementation. * @param idGenerator The ID generator used to create the authenticator ID. * @param clock The clock implementation. @@ -147,7 +147,7 @@ object CookieAuthenticator extends Logger { */ class CookieAuthenticatorService( settings: CookieAuthenticatorSettings, - dao: Option[AuthenticatorDAO[CookieAuthenticator]], + repository: Option[AuthenticatorRepository[CookieAuthenticator]], fingerprintGenerator: FingerprintGenerator, idGenerator: IDGenerator, clock: Clock)(implicit val executionContext: ExecutionContext) @@ -192,7 +192,7 @@ class CookieAuthenticatorService( }).flatMap { fingerprint => request.cookies.get(settings.cookieName) match { case Some(cookie) => - (dao match { + (repository match { case Some(d) => d.find(cookie.value) case None => unserialize(cookie.value)(settings) match { case Success(authenticator) => Future.successful(Some(authenticator)) @@ -224,7 +224,7 @@ class CookieAuthenticatorService( * @return The serialized authenticator value. */ override def init(authenticator: CookieAuthenticator)(implicit request: RequestHeader): Future[Cookie] = { - (dao match { + (repository match { case Some(d) => d.add(authenticator).map(_.id) case None => Future.successful(serialize(authenticator)(settings)) }).map { value => @@ -298,7 +298,7 @@ class CookieAuthenticatorService( override def update(authenticator: CookieAuthenticator, result: Result)( implicit request: RequestHeader): Future[AuthenticatorResult] = { - (dao match { + (repository match { case Some(d) => d.update(authenticator).map(_ => AuthenticatorResult(result)) case None => Future.successful(AuthenticatorResult(result.withCookies(Cookie( name = settings.cookieName, @@ -328,7 +328,7 @@ class CookieAuthenticatorService( * @return The serialized expression of the authenticator. */ override def renew(authenticator: CookieAuthenticator)(implicit request: RequestHeader): Future[Cookie] = { - (dao match { + (repository match { case Some(d) => d.remove(authenticator.id) case None => Future.successful(()) }).flatMap { _ => @@ -369,7 +369,7 @@ class CookieAuthenticatorService( override def discard(authenticator: CookieAuthenticator, result: Result)( implicit request: RequestHeader): Future[AuthenticatorResult] = { - (dao match { + (repository match { case Some(d) => d.remove(authenticator.id) case None => Future.successful(()) }).map { _ => diff --git a/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/JWTAuthenticator.scala b/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/JWTAuthenticator.scala index 78dc6da54..3b06031e6 100644 --- a/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/JWTAuthenticator.scala +++ b/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/JWTAuthenticator.scala @@ -20,13 +20,13 @@ import com.atlassian.jwt.core.writer.{ JsonSmartJwtJsonBuilder, NimbusJwtWriterF import com.mohiva.play.silhouette._ import com.mohiva.play.silhouette.api.Authenticator.Implicits._ import com.mohiva.play.silhouette.api.exceptions._ +import com.mohiva.play.silhouette.api.repositories.AuthenticatorRepository import com.mohiva.play.silhouette.api.services.AuthenticatorService._ import com.mohiva.play.silhouette.api.services.{ AuthenticatorResult, AuthenticatorService } import com.mohiva.play.silhouette.api.util.{ Base64, Clock, IDGenerator } import com.mohiva.play.silhouette.api.{ ExpirableAuthenticator, Logger, LoginInfo, StorableAuthenticator } import com.mohiva.play.silhouette.impl.authenticators.JWTAuthenticator._ import com.mohiva.play.silhouette.impl.authenticators.JWTAuthenticatorService._ -import com.mohiva.play.silhouette.impl.daos.AuthenticatorDAO import com.nimbusds.jose.JWSObject import com.nimbusds.jose.crypto.MACVerifier import com.nimbusds.jwt.JWTClaimsSet @@ -215,14 +215,14 @@ object JWTAuthenticator { * that you will loose the possibility to invalidate a JWT. * * @param settings The authenticator settings. - * @param dao The DAO to store the authenticator. Set it to None to use a stateless approach. + * @param repository The repository to persist the authenticator. Set it to None to use a stateless approach. * @param idGenerator The ID generator used to create the authenticator ID. * @param clock The clock implementation. * @param executionContext The execution context to handle the asynchronous operations. */ class JWTAuthenticatorService( settings: JWTAuthenticatorSettings, - dao: Option[AuthenticatorDAO[JWTAuthenticator]], + repository: Option[AuthenticatorRepository[JWTAuthenticator]], idGenerator: IDGenerator, clock: Clock)(implicit val executionContext: ExecutionContext) extends AuthenticatorService[JWTAuthenticator] @@ -261,7 +261,7 @@ class JWTAuthenticatorService( override def retrieve(implicit request: RequestHeader): Future[Option[JWTAuthenticator]] = { Future.from(Try(request.headers.get(settings.headerName))).flatMap { case Some(token) => unserialize(token)(settings) match { - case Success(authenticator) => dao.fold(Future.successful(Option(authenticator)))(_.find(authenticator.id)) + case Success(authenticator) => repository.fold(Future.successful(Option(authenticator)))(_.find(authenticator.id)) case Failure(e) => logger.info(e.getMessage, e) Future.successful(None) @@ -281,7 +281,7 @@ class JWTAuthenticatorService( * @return The serialized authenticator value. */ override def init(authenticator: JWTAuthenticator)(implicit request: RequestHeader): Future[String] = { - dao.fold(Future.successful(authenticator))(_.add(authenticator)).map { a => + repository.fold(Future.successful(authenticator))(_.add(authenticator)).map { a => serialize(a)(settings) }.recover { case e => throw new AuthenticatorInitializationException(InitError.format(ID, authenticator), e) @@ -339,7 +339,7 @@ class JWTAuthenticatorService( override def update(authenticator: JWTAuthenticator, result: Result)( implicit request: RequestHeader): Future[AuthenticatorResult] = { - dao.fold(Future.successful(authenticator))(_.update(authenticator)).map { a => + repository.fold(Future.successful(authenticator))(_.update(authenticator)).map { a => AuthenticatorResult(result.withHeaders(settings.headerName -> serialize(a)(settings))) }.recover { case e => throw new AuthenticatorUpdateException(UpdateError.format(ID, authenticator), e) @@ -358,7 +358,7 @@ class JWTAuthenticatorService( * @return The serialized expression of the authenticator. */ override def renew(authenticator: JWTAuthenticator)(implicit request: RequestHeader): Future[String] = { - dao.fold(Future.successful(()))(_.remove(authenticator.id)).flatMap { _ => + repository.fold(Future.successful(()))(_.remove(authenticator.id)).flatMap { _ => create(authenticator.loginInfo).flatMap(init) }.recover { case e => throw new AuthenticatorRenewalException(RenewError.format(ID, authenticator), e) @@ -394,7 +394,7 @@ class JWTAuthenticatorService( override def discard(authenticator: JWTAuthenticator, result: Result)( implicit request: RequestHeader): Future[AuthenticatorResult] = { - dao.fold(Future.successful(()))(_.remove(authenticator.id)).map { _ => + repository.fold(Future.successful(()))(_.remove(authenticator.id)).map { _ => AuthenticatorResult(result) }.recover { case e => throw new AuthenticatorDiscardingException(DiscardError.format(ID, authenticator), e) diff --git a/silhouette/build.sbt b/silhouette/build.sbt index 9c0b76146..ac1e725e9 100644 --- a/silhouette/build.sbt +++ b/silhouette/build.sbt @@ -7,8 +7,8 @@ libraryDependencies ++= Seq( Library.jwtCore, Library.jwtApi, Library.Play.specs2 % Test, - Library.Specs2.matcherExtra % Test, - Library.mockito % Test, + Library.Play.Specs2.matcherExtra % Test, + Library.Play.Specs2.mock % Test, Library.scalaGuice % Test, Library.akkaTestkit % Test ) diff --git a/silhouette/test/com/mohiva/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala b/silhouette/test/com/mohiva/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala index f17d0b01c..2e5a08b44 100644 --- a/silhouette/test/com/mohiva/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala +++ b/silhouette/test/com/mohiva/play/silhouette/impl/authenticators/BearerTokenAuthenticatorSpec.scala @@ -18,10 +18,10 @@ package com.mohiva.play.silhouette.impl.authenticators import com.mohiva.play.silhouette.api.Authenticator.Implicits._ import com.mohiva.play.silhouette.api.LoginInfo import com.mohiva.play.silhouette.api.exceptions._ +import com.mohiva.play.silhouette.api.repositories.AuthenticatorRepository import com.mohiva.play.silhouette.api.services.AuthenticatorService._ import com.mohiva.play.silhouette.api.util.{ Clock, IDGenerator } import com.mohiva.play.silhouette.impl.authenticators.BearerTokenAuthenticatorService._ -import com.mohiva.play.silhouette.impl.daos.AuthenticatorDAO import org.joda.time.DateTime import org.specs2.control.NoLanguageFeatures import org.specs2.mock.Mockito @@ -123,7 +123,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N "return None if no authenticator is stored for the header" in new Context { implicit val request = FakeRequest().withHeaders(settings.headerName -> authenticator.id) - dao.find(authenticator.id) returns Future.successful(None) + repository.find(authenticator.id) returns Future.successful(None) await(service.retrieve) must beNone } @@ -131,7 +131,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N "return authenticator if an authenticator is stored for the header" in new Context { implicit val request = FakeRequest().withHeaders(settings.headerName -> authenticator.id) - dao.find(authenticator.id) returns Future.successful(Some(authenticator)) + repository.find(authenticator.id) returns Future.successful(Some(authenticator)) await(service.retrieve) must beSome(authenticator) } @@ -139,7 +139,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().withHeaders(settings.headerName -> authenticator.id) - dao.find(authenticator.id) returns Future.failed(new RuntimeException("Cannot find authenticator")) + repository.find(authenticator.id) returns Future.failed(new RuntimeException("Cannot find authenticator")) await(service.retrieve) must throwA[AuthenticatorRetrievalException].like { case e => @@ -150,17 +150,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 { - dao.add(any) answers { p => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) } + repository.add(any) answers { p => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) } implicit val request = FakeRequest() val token = await(service.init(authenticator)) token must be equalTo authenticator.id - there was one(dao).add(authenticator) + there was one(repository).add(authenticator) } "throws an AuthenticatorInitializationException exception if an error occurred during initialization" in new Context { - dao.add(any) returns Future.failed(new Exception("Cannot store authenticator")) + repository.add(any) returns Future.failed(new Exception("Cannot store authenticator")) implicit val request = FakeRequest() @@ -229,17 +229,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 { - dao.update(any) returns Future.successful(authenticator) + repository.update(any) returns Future.successful(authenticator) implicit val request = FakeRequest() await(service.update(authenticator, Results.Ok)) - there was one(dao).update(authenticator) + there was one(repository).update(authenticator) } "return the result if the authenticator could be stored in backing store" in new Context { - dao.update(any) answers { p => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) } + repository.update(any) answers { p => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) } implicit val request = FakeRequest() val result = service.update(authenticator, Results.Ok) @@ -248,7 +248,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N } "throws an AuthenticatorUpdateException exception if an error occurred during update" in new Context { - dao.update(any) returns Future.failed(new Exception("Cannot store authenticator")) + repository.update(any) returns Future.failed(new Exception("Cannot store authenticator")) implicit val request = FakeRequest() @@ -265,14 +265,14 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N val now = new DateTime val id = "new-test-id" - dao.remove(authenticator.id) returns Future.successful(()) - dao.add(any) answers { p => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) } + repository.remove(authenticator.id) returns Future.successful(()) + repository.add(any) answers { p => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) } idGenerator.generate returns Future.successful(id) clock.now returns now await(service.renew(authenticator, Results.Ok)) - there was one(dao).remove(authenticator.id) + there was one(repository).remove(authenticator.id) } "renew the authenticator and return the response with a new bearer token" in new Context { @@ -280,8 +280,8 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N val now = new DateTime val id = "new-test-id" - dao.remove(any) returns Future.successful(()) - dao.add(any) answers { p => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) } + repository.remove(any) returns Future.successful(()) + repository.add(any) answers { p => Future.successful(p.asInstanceOf[BearerTokenAuthenticator]) } idGenerator.generate returns Future.successful(id) clock.now returns now @@ -295,8 +295,8 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N val now = new DateTime val id = "new-test-id" - dao.remove(any) returns Future.successful(()) - dao.add(any) returns Future.failed(new Exception("Cannot store authenticator")) + 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 @@ -311,18 +311,18 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N "remove authenticator from backing store" in new Context { implicit val request = FakeRequest() - dao.remove(authenticator.id) returns Future.successful(authenticator) + repository.remove(authenticator.id) returns Future.successful(authenticator) await(service.discard(authenticator, Results.Ok)) - there was one(dao).remove(authenticator.id) + there was one(repository).remove(authenticator.id) } "throws an AuthenticatorDiscardingException exception if an error occurred during discarding" in new Context { implicit val request = FakeRequest() val okResult = Results.Ok - dao.remove(authenticator.id) returns Future.failed(new Exception("Cannot remove authenticator")) + repository.remove(authenticator.id) returns Future.failed(new Exception("Cannot remove authenticator")) await(service.discard(authenticator, okResult)) must throwA[AuthenticatorDiscardingException].like { case e => @@ -337,9 +337,9 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N trait Context extends Scope { /** - * The backing store implementation. + * The repository implementation. */ - lazy val dao: AuthenticatorDAO[BearerTokenAuthenticator] = mock[AuthenticatorDAO[BearerTokenAuthenticator]] + lazy val repository: AuthenticatorRepository[BearerTokenAuthenticator] = mock[AuthenticatorRepository[BearerTokenAuthenticator]] /** * The ID generator implementation. @@ -363,7 +363,7 @@ class BearerTokenAuthenticatorSpec extends PlaySpecification with Mockito with N /** * The authenticator service instance to test. */ - lazy val service = new BearerTokenAuthenticatorService(settings, dao, idGenerator, clock) + lazy val service = new BearerTokenAuthenticatorService(settings, repository, idGenerator, clock) /** * The login info. diff --git a/silhouette/test/com/mohiva/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala b/silhouette/test/com/mohiva/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala index b0b1a6608..af28b44b0 100644 --- a/silhouette/test/com/mohiva/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala +++ b/silhouette/test/com/mohiva/play/silhouette/impl/authenticators/CookieAuthenticatorSpec.scala @@ -20,11 +20,11 @@ import java.util.regex.Pattern import com.mohiva.play.silhouette.api.Authenticator.Implicits._ import com.mohiva.play.silhouette.api.LoginInfo import com.mohiva.play.silhouette.api.exceptions._ +import com.mohiva.play.silhouette.api.repositories.AuthenticatorRepository import com.mohiva.play.silhouette.api.services.AuthenticatorService._ import com.mohiva.play.silhouette.api.util.{ Clock, FingerprintGenerator, IDGenerator } import com.mohiva.play.silhouette.impl.authenticators.CookieAuthenticator._ import com.mohiva.play.silhouette.impl.authenticators.CookieAuthenticatorService._ -import com.mohiva.play.silhouette.impl.daos.AuthenticatorDAO import org.joda.time.DateTime import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.MatchResult @@ -106,7 +106,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang fingerprintGenerator.generate(any) returns "test" settings.useFingerprinting returns true - await(service(Some(dao)).create(loginInfo)).fingerprint must beSome("test") + await(service(Some(repository)).create(loginInfo)).fingerprint must beSome("test") } "return a non fingerprinted authenticator" in new Context { @@ -116,7 +116,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang clock.now returns new DateTime settings.useFingerprinting returns false - await(service(Some(dao)).create(loginInfo)).fingerprint must beNone + await(service(Some(repository)).create(loginInfo)).fingerprint must beNone } "return an authenticator with the generated ID" in new Context { @@ -126,7 +126,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang idGenerator.generate returns Future.successful(id) clock.now returns new DateTime - await(service(Some(dao)).create(loginInfo)).id must be equalTo id + await(service(Some(repository)).create(loginInfo)).id must be equalTo id } "return an authenticator with the current date as lastUsedDateTime" in new Context { @@ -136,7 +136,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang idGenerator.generate returns Future.successful("test-id") clock.now returns now - await(service(Some(dao)).create(loginInfo)).lastUsedDateTime must be equalTo now + await(service(Some(repository)).create(loginInfo)).lastUsedDateTime must be equalTo now } "return an authenticator which expires in 12 hours(default value)" in new Context { @@ -146,7 +146,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang idGenerator.generate returns Future.successful("test-id") clock.now returns now - await(service(Some(dao)).create(loginInfo)).expirationDateTime must be equalTo now + 12.hours + await(service(Some(repository)).create(loginInfo)).expirationDateTime must be equalTo now + 12.hours } "return an authenticator which expires in 6 hours" in new Context { @@ -158,7 +158,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang idGenerator.generate returns Future.successful("test-id") clock.now returns now - await(service(Some(dao)).create(loginInfo)).expirationDateTime must be equalTo now + sixHours + await(service(Some(repository)).create(loginInfo)).expirationDateTime must be equalTo now + sixHours } "throws an AuthenticatorCreationException exception if an error occurred during creation" in new Context { @@ -166,7 +166,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang idGenerator.generate returns Future.failed(new Exception("Could not generate ID")) - await(service(Some(dao)).create(loginInfo)) must throwA[AuthenticatorCreationException].like { + await(service(Some(repository)).create(loginInfo)) must throwA[AuthenticatorCreationException].like { case e => e.getMessage must startWith(CreateError.format(ID, "")) } @@ -177,22 +177,22 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "return None if no authenticator cookie exists" in new Context { implicit val request = FakeRequest() - await(service(Some(dao)).retrieve) must beNone + await(service(Some(repository)).retrieve) must beNone } "[stateful] return None if no authenticator for the cookie is stored in backing store" in new Context { implicit val request = FakeRequest().withCookies(Cookie(settings.cookieName, authenticator.id)) - dao.find(authenticator.id) returns Future.successful(None) + repository.find(authenticator.id) returns Future.successful(None) - await(service(Some(dao)).retrieve) must beNone + await(service(Some(repository)).retrieve) must beNone } "[stateless] return None if no authenticator could be unserialized from cookie" in new WithApplication with Context { implicit val request = FakeRequest().withCookies(Cookie(settings.cookieName, Crypto.encryptAES("invalid"))) await(service(None).retrieve) must beNone - there was no(dao).find(any) + there was no(repository).find(any) } "[stateful] return None if authenticator fingerprint doesn't match current fingerprint" in new Context { @@ -201,9 +201,9 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang fingerprintGenerator.generate(any) returns "false" settings.useFingerprinting returns true authenticator.fingerprint returns Some("test") - dao.find(authenticator.id) returns Future.successful(Some(authenticator)) + repository.find(authenticator.id) returns Future.successful(Some(authenticator)) - await(service(Some(dao)).retrieve) must beNone + await(service(Some(repository)).retrieve) must beNone } "[stateless] return None if authenticator fingerprint doesn't match current fingerprint" in new WithApplication with Context { @@ -214,7 +214,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang implicit val request = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator)(settings))) await(service(None).retrieve) must beNone - there was no(dao).find(any) + there was no(repository).find(any) } "[stateful] return authenticator if authenticator fingerprint matches current fingerprint" in new Context { @@ -223,9 +223,9 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang fingerprintGenerator.generate(any) returns "test" settings.useFingerprinting returns true authenticator.fingerprint returns Some("test") - dao.find(authenticator.id) returns Future.successful(Some(authenticator)) + repository.find(authenticator.id) returns Future.successful(Some(authenticator)) - await(service(Some(dao)).retrieve) must beSome(authenticator) + await(service(Some(repository)).retrieve) must beSome(authenticator) } "[stateless] return authenticator if authenticator fingerprint matches current fingerprint" in new WithApplication with Context { @@ -236,16 +236,16 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang implicit val request = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator)(settings))) await(service(None).retrieve) must beSome(authenticator) - there was no(dao).find(any) + there was no(repository).find(any) } "[stateful] return authenticator if fingerprinting is disabled" in new Context { implicit val request = FakeRequest().withCookies(Cookie(settings.cookieName, authenticator.id)) settings.useFingerprinting returns false - dao.find(authenticator.id) returns Future.successful(Some(authenticator)) + repository.find(authenticator.id) returns Future.successful(Some(authenticator)) - await(service(Some(dao)).retrieve) must beSome(authenticator) + await(service(Some(repository)).retrieve) must beSome(authenticator) } "[stateless] return authenticator if fingerprinting is disabled" in new WithApplication with Context { @@ -253,7 +253,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang implicit val request = FakeRequest().withCookies(Cookie(settings.cookieName, serialize(authenticator)(settings))) - dao.find(authenticator.id) returns Future.successful(Some(authenticator)) + repository.find(authenticator.id) returns Future.successful(Some(authenticator)) await(service(None).retrieve) must beSome(authenticator) } @@ -263,9 +263,9 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang fingerprintGenerator.generate throws new RuntimeException("Could not generate fingerprint") settings.useFingerprinting returns true - dao.find(authenticator.id) returns Future.successful(Some(authenticator)) + repository.find(authenticator.id) returns Future.successful(Some(authenticator)) - await(service(Some(dao)).retrieve) must throwA[AuthenticatorRetrievalException].like { + await(service(Some(repository)).retrieve) must throwA[AuthenticatorRetrievalException].like { case e => e.getMessage must startWith(RetrieveError.format(ID, "")) } @@ -274,12 +274,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 { - dao.add(any) returns Future.successful(authenticator) + repository.add(any) returns Future.successful(authenticator) implicit val request = FakeRequest() - await(service(Some(dao)).init(authenticator)) must be equalTo statefulCookie - there was one(dao).add(any) + await(service(Some(repository)).init(authenticator)) must be equalTo statefulCookie + there was one(repository).add(any) } "[stateless] return a cookie with a serialized authenticator" in new WithApplication with Context { @@ -288,15 +288,15 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang val cookie = await(service(None).init(authenticator)) unserialize(cookie.value)(settings) must be equalTo unserialize(statelessCookie.value)(settings) - there was no(dao).add(any) + there was no(repository).add(any) } "throws an AuthenticatorInitializationException exception if an error occurred during initialization" in new Context { - dao.add(any) returns Future.failed(new Exception("Cannot store authenticator")) + repository.add(any) returns Future.failed(new Exception("Cannot store authenticator")) implicit val request = FakeRequest() - await(service(Some(dao)).init(authenticator)) must throwA[AuthenticatorInitializationException].like { + await(service(Some(repository)).init(authenticator)) must throwA[AuthenticatorInitializationException].like { case e => e.getMessage must startWith(InitError.format(ID, "")) } @@ -306,7 +306,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "The result `embed` method of the service" should { "return the response with a cookie" in new Context { implicit val request = FakeRequest() - val result = service(Some(dao)).embed(statefulCookie, Results.Ok) + val result = service(Some(repository)).embed(statefulCookie, Results.Ok) cookies(result).get(settings.cookieName) should beSome[Cookie].which( statefulResponseCookieMatcher(authenticator.id) @@ -316,19 +316,19 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang "The request `embed` method of the service" should { "return the request with a cookie" in new Context { - val request = service(Some(dao)).embed(statefulCookie, FakeRequest()) + val request = service(Some(repository)).embed(statefulCookie, FakeRequest()) request.cookies.get(settings.cookieName) should beSome[Cookie].which(requestCookieMatcher(authenticator.id)) } "override an existing cookie" in new Context { - val request = service(Some(dao)).embed(statefulCookie, FakeRequest().withCookies(Cookie(settings.cookieName, "test"))) + val request = service(Some(repository)).embed(statefulCookie, FakeRequest().withCookies(Cookie(settings.cookieName, "test"))) request.cookies.get(settings.cookieName) should beSome[Cookie].which(requestCookieMatcher(authenticator.id)) } "keep non authenticator related cookies" in new Context { - val request = service(Some(dao)).embed(statefulCookie, FakeRequest().withCookies(Cookie("test", "test"))) + val request = service(Some(repository)).embed(statefulCookie, FakeRequest().withCookies(Cookie("test", "test"))) request.cookies.get(settings.cookieName) should beSome[Cookie].which(requestCookieMatcher(authenticator.id)) request.cookies.get("test") should beSome[Cookie].which { c => @@ -343,7 +343,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang settings.authenticatorIdleTimeout returns Some(1 second) clock.now returns DateTime.now - service(Some(dao)).touch(authenticator) must beLeft[CookieAuthenticator].like { + service(Some(repository)).touch(authenticator) must beLeft[CookieAuthenticator].like { case a => a.lastUsedDateTime must be equalTo clock.now } @@ -353,7 +353,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang settings.authenticatorIdleTimeout returns None clock.now returns DateTime.now - service(Some(dao)).touch(authenticator) must beRight[CookieAuthenticator].like { + service(Some(repository)).touch(authenticator) must beRight[CookieAuthenticator].like { case a => a.lastUsedDateTime must be equalTo authenticator.lastUsedDateTime } @@ -362,20 +362,20 @@ 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 { - dao.update(any) returns Future.successful(authenticator) + repository.update(any) returns Future.successful(authenticator) implicit val request = FakeRequest() - await(service(Some(dao)).update(authenticator, Results.Ok)) + await(service(Some(repository)).update(authenticator, Results.Ok)) - there was one(dao).update(authenticator) + there was one(repository).update(authenticator) } "[stateful] return the result if the authenticator could be stored in backing store" in new Context { - dao.update(any) answers { p => Future.successful(p.asInstanceOf[CookieAuthenticator]) } + repository.update(any) answers { p => Future.successful(p.asInstanceOf[CookieAuthenticator]) } implicit val request = FakeRequest() - val result = service(Some(dao)).update(authenticator, Results.Ok) + val result = service(Some(repository)).update(authenticator, Results.Ok) status(result) must be equalTo OK } @@ -386,15 +386,15 @@ 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(dao).update(authenticator) + there was no(repository).update(authenticator) } "throws an AuthenticatorUpdateException exception if an error occurred during update" in new Context { - dao.update(any) returns Future.failed(new Exception("Cannot store authenticator")) + repository.update(any) returns Future.failed(new Exception("Cannot store authenticator")) implicit val request = FakeRequest() - await(service(Some(dao)).update(authenticator, Results.Ok)) must throwA[AuthenticatorUpdateException].like { + await(service(Some(repository)).update(authenticator, Results.Ok)) must throwA[AuthenticatorUpdateException].like { case e => e.getMessage must startWith(UpdateError.format(ID, "")) } @@ -407,14 +407,14 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang val now = new DateTime val id = "new-test-id" - dao.remove(authenticator.id) returns Future.successful(()) - dao.add(any) answers { p => Future.successful(p.asInstanceOf[CookieAuthenticator]) } + repository.remove(authenticator.id) returns Future.successful(()) + repository.add(any) answers { p => Future.successful(p.asInstanceOf[CookieAuthenticator]) } idGenerator.generate returns Future.successful(id) clock.now returns now - await(service(Some(dao)).renew(authenticator, Results.Ok)) + await(service(Some(repository)).renew(authenticator, Results.Ok)) - there was one(dao).remove(authenticator.id) + there was one(repository).remove(authenticator.id) } "[stateful] renew the authenticator and return the response with the updated cookie value" in new Context { @@ -422,15 +422,15 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang val now = new DateTime val id = "new-test-id" - dao.remove(any) returns Future.successful(()) - dao.add(any) answers { p => Future.successful(p.asInstanceOf[CookieAuthenticator]) } + repository.remove(any) returns Future.successful(()) + repository.add(any) answers { p => Future.successful(p.asInstanceOf[CookieAuthenticator]) } idGenerator.generate returns Future.successful(id) clock.now returns now - val result = service(Some(dao)).renew(authenticator, Results.Ok) + val result = service(Some(repository)).renew(authenticator, Results.Ok) cookies(result).get(settings.cookieName) should beSome[Cookie].which(statefulResponseCookieMatcher(id)) - there was one(dao).add(any) + there was one(repository).add(any) } "[stateless] renew the authenticator and return the response with the updated cookie value" in new WithApplication with Context { @@ -446,7 +446,7 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang cookies(result).get(settings.cookieName) should beSome[Cookie].which(statelessResponseCookieMatcher( authenticator.copy(id = id, lastUsedDateTime = now, expirationDateTime = now + settings.authenticatorExpiry) )) - there was no(dao).add(any) + there was no(repository).add(any) } "throws an AuthenticatorRenewalException exception if an error occurred during renewal" in new Context { @@ -454,12 +454,12 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang val now = new DateTime val id = "new-test-id" - dao.remove(any) returns Future.successful(()) - dao.add(any) returns Future.failed(new Exception("Cannot store authenticator")) + 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 - await(service(Some(dao)).renew(authenticator, Results.Ok)) must throwA[AuthenticatorRenewalException].like { + await(service(Some(repository)).renew(authenticator, Results.Ok)) must throwA[AuthenticatorRenewalException].like { case e => e.getMessage must startWith(RenewError.format(ID, "")) } @@ -470,9 +470,9 @@ 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() - dao.remove(any) returns Future.successful(()) + repository.remove(any) returns Future.successful(()) - val result = service(Some(dao)).discard(authenticator, Results.Ok.withCookies(statefulCookie)) + val result = service(Some(repository)).discard(authenticator, Results.Ok.withCookies(statefulCookie)) cookies(result).get(settings.cookieName) should beSome[Cookie].which { c => c.name must be equalTo settings.cookieName @@ -482,7 +482,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(dao).remove(authenticator.id) + there was one(repository).remove(authenticator.id) } "[stateless] discard the cookie from response" in new WithApplication with Context { @@ -497,16 +497,16 @@ 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(dao).remove(authenticator.id) + there was no(repository).remove(authenticator.id) } "throws an AuthenticatorDiscardingException exception if an error occurred during discarding" in new Context { implicit val request = FakeRequest() val okResult = Results.Ok - dao.remove(any) returns Future.failed(new Exception("Cannot store authenticator")) + repository.remove(any) returns Future.failed(new Exception("Cannot store authenticator")) - await(service(Some(dao)).discard(authenticator, okResult)) must throwA[AuthenticatorDiscardingException].like { + await(service(Some(repository)).discard(authenticator, okResult)) must throwA[AuthenticatorDiscardingException].like { case e => e.getMessage must startWith(DiscardError.format(ID, "")) } @@ -519,9 +519,9 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang trait Context extends Scope { /** - * The backing store implementation. + * The repository implementation. */ - lazy val dao: AuthenticatorDAO[CookieAuthenticator] = mock[AuthenticatorDAO[CookieAuthenticator]] + lazy val repository: AuthenticatorRepository[CookieAuthenticator] = mock[AuthenticatorRepository[CookieAuthenticator]] /** * The ID generator implementation. @@ -556,8 +556,8 @@ class CookieAuthenticatorSpec extends PlaySpecification with Mockito with NoLang /** * The authenticator service instance to test. */ - lazy val service = (dao: Option[AuthenticatorDAO[CookieAuthenticator]]) => - new CookieAuthenticatorService(settings, dao, fingerprintGenerator, idGenerator, clock) + lazy val service = (repository: Option[AuthenticatorRepository[CookieAuthenticator]]) => + new CookieAuthenticatorService(settings, repository, fingerprintGenerator, idGenerator, clock) /** * The login info. diff --git a/silhouette/test/com/mohiva/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala b/silhouette/test/com/mohiva/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala index d20402f71..3d520dc22 100644 --- a/silhouette/test/com/mohiva/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala +++ b/silhouette/test/com/mohiva/play/silhouette/impl/authenticators/JWTAuthenticatorSpec.scala @@ -20,11 +20,11 @@ import java.util.regex.Pattern import com.mohiva.play.silhouette.api.Authenticator.Implicits._ import com.mohiva.play.silhouette.api.LoginInfo import com.mohiva.play.silhouette.api.exceptions._ +import com.mohiva.play.silhouette.api.repositories.AuthenticatorRepository import com.mohiva.play.silhouette.api.services.AuthenticatorService._ import com.mohiva.play.silhouette.api.util.{ Base64, Clock, IDGenerator } import com.mohiva.play.silhouette.impl.authenticators.JWTAuthenticator._ import com.mohiva.play.silhouette.impl.authenticators.JWTAuthenticatorService._ -import com.mohiva.play.silhouette.impl.daos.AuthenticatorDAO import org.joda.time.DateTime import org.specs2.control.NoLanguageFeatures import org.specs2.matcher.JsonMatchers @@ -267,20 +267,20 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch "return None if DAO is enabled an no authenticator is stored for the header" in new Context { implicit val request = FakeRequest().withHeaders(settings.headerName -> "not-stored") - dao.find(authenticator.id) returns Future.successful(None) + repository.find(authenticator.id) returns Future.successful(None) - await(service(Some(dao)).retrieve) must beNone + await(service(Some(repository)).retrieve) must beNone } "return authenticator if DAO is enabled and an authenticator is stored for the header" in new WithApplication with Context { implicit val request = FakeRequest().withHeaders(settings.headerName -> serialize(authenticator)(settings)) - dao.find(authenticator.id) returns Future.successful(Some(authenticator.copy( + repository.find(authenticator.id) returns Future.successful(Some(authenticator.copy( expirationDateTime = authenticator.expirationDateTime.withMillisOfSecond(0), lastUsedDateTime = authenticator.lastUsedDateTime.withMillisOfSecond(0) ))) - await(service(Some(dao)).retrieve) must beSome(authenticator.copy( + await(service(Some(repository)).retrieve) must beSome(authenticator.copy( expirationDateTime = authenticator.expirationDateTime.withMillisOfSecond(0), lastUsedDateTime = authenticator.lastUsedDateTime.withMillisOfSecond(0) )) @@ -293,15 +293,15 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch expirationDateTime = authenticator.expirationDateTime.withMillisOfSecond(0), lastUsedDateTime = authenticator.lastUsedDateTime.withMillisOfSecond(0) )) - there was no(dao).find(any) + there was no(repository).find(any) } "throws an AuthenticatorRetrievalException exception if an error occurred during retrieval" in new WithApplication with Context { implicit val request = FakeRequest().withHeaders(settings.headerName -> serialize(authenticator)(settings)) - dao.find(authenticator.id) returns Future.failed(new RuntimeException("Cannot find authenticator")) + repository.find(authenticator.id) returns Future.failed(new RuntimeException("Cannot find authenticator")) - await(service(Some(dao)).retrieve) must throwA[AuthenticatorRetrievalException].like { + await(service(Some(repository)).retrieve) must throwA[AuthenticatorRetrievalException].like { case e => e.getMessage must startWith(RetrieveError.format(ID, "")) } @@ -310,13 +310,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 { - dao.add(any) answers { p => Future.successful(p.asInstanceOf[JWTAuthenticator]) } + repository.add(any) answers { p => Future.successful(p.asInstanceOf[JWTAuthenticator]) } implicit val request = FakeRequest() - val token = await(service(Some(dao)).init(authenticator)) + val token = await(service(Some(repository)).init(authenticator)) unserialize(token)(settings).get must be equalTo authenticator - there was one(dao).add(any) + there was one(repository).add(any) } "return the token if DAO is disabled" in new WithApplication with Context { @@ -325,16 +325,16 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch val token = await(service(None).init(authenticator)) unserialize(token)(settings).get must be equalTo authenticator - there was no(dao).add(any) + there was no(repository).add(any) } "throws an AuthenticatorInitializationException exception if an error occurred during initialization" in new Context { - dao.add(any) returns Future.failed(new Exception("Cannot store authenticator")) + repository.add(any) returns Future.failed(new Exception("Cannot store authenticator")) implicit val request = FakeRequest() val okResult = Future.successful(Results.Ok) - await(service(Some(dao)).init(authenticator)) must throwA[AuthenticatorInitializationException].like { + await(service(Some(repository)).init(authenticator)) must throwA[AuthenticatorInitializationException].like { case e => e.getMessage must startWith(InitError.format(ID, "")) } @@ -346,7 +346,7 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch implicit val request = FakeRequest() val token = serialize(authenticator)(settings) - val result = service(Some(dao)).embed(token, Results.Ok) + val result = service(Some(repository)).embed(token, Results.Ok) header(settings.headerName, result) should beSome(token) } @@ -355,21 +355,21 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch "The request `embed` method of the service" should { "return the request with a header " in new WithApplication with Context { val token = serialize(authenticator)(settings) - val request = service(Some(dao)).embed(token, FakeRequest()) + val request = service(Some(repository)).embed(token, FakeRequest()) unserialize(request.headers.get(settings.headerName).get)(settings).get must be equalTo authenticator } "override an existing token" in new WithApplication with Context { val token = serialize(authenticator)(settings) - val request = service(Some(dao)).embed(token, FakeRequest().withHeaders(settings.headerName -> "test")) + val request = service(Some(repository)).embed(token, FakeRequest().withHeaders(settings.headerName -> "test")) unserialize(request.headers.get(settings.headerName).get)(settings).get must be equalTo authenticator } "keep non authenticator related headers" in new WithApplication with Context { val token = serialize(authenticator)(settings) - val request = service(Some(dao)).embed(token, FakeRequest().withHeaders("test" -> "test")) + val request = service(Some(repository)).embed(token, FakeRequest().withHeaders("test" -> "test")) unserialize(request.headers.get(settings.headerName).get)(settings).get must be equalTo authenticator request.headers.get("test") should beSome("test") @@ -400,43 +400,43 @@ 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 { - dao.update(any) returns Future.successful(authenticator) + repository.update(any) returns Future.successful(authenticator) implicit val request = FakeRequest() - await(service(Some(dao)).update(authenticator, Results.Ok)) + await(service(Some(repository)).update(authenticator, Results.Ok)) - there was one(dao).update(authenticator) + there was one(repository).update(authenticator) } "return the result if the authenticator could be stored in backing store" in new WithApplication with Context { - dao.update(any) answers { p => Future.successful(p.asInstanceOf[JWTAuthenticator]) } + repository.update(any) answers { p => Future.successful(p.asInstanceOf[JWTAuthenticator]) } implicit val request = FakeRequest() - val result = service(Some(dao)).update(authenticator, Results.Ok) + val result = service(Some(repository)).update(authenticator, Results.Ok) status(result) must be equalTo OK unserialize(header(settings.headerName, result).get)(settings).get must be equalTo authenticator - there was one(dao).update(authenticator) + there was one(repository).update(authenticator) } "return the result if backing store is disabled" in new WithApplication with Context { - dao.update(any) answers { p => Future.successful(p.asInstanceOf[JWTAuthenticator]) } + repository.update(any) answers { p => Future.successful(p.asInstanceOf[JWTAuthenticator]) } implicit val request = FakeRequest() val result = service(None).update(authenticator, Results.Ok) status(result) must be equalTo OK unserialize(header(settings.headerName, result).get)(settings).get must be equalTo authenticator - there was no(dao).update(any) + there was no(repository).update(any) } "throws an AuthenticatorUpdateException exception if an error occurred during update" in new WithApplication with Context { - dao.update(any) returns Future.failed(new Exception("Cannot store authenticator")) + repository.update(any) returns Future.failed(new Exception("Cannot store authenticator")) implicit val request = FakeRequest() - await(service(Some(dao)).update(authenticator, Results.Ok)) must throwA[AuthenticatorUpdateException].like { + await(service(Some(repository)).update(authenticator, Results.Ok)) must throwA[AuthenticatorUpdateException].like { case e => e.getMessage must startWith(UpdateError.format(ID, "")) } @@ -449,12 +449,12 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch val now = new DateTime(2015, 2, 25, 19, 0, 0, 0) val id = "new-test-id" - dao.remove(any) returns Future.successful(()) - dao.add(any) answers { p => Future.successful(p.asInstanceOf[JWTAuthenticator]) } + repository.remove(any) returns Future.successful(()) + repository.add(any) answers { p => Future.successful(p.asInstanceOf[JWTAuthenticator]) } idGenerator.generate returns Future.successful(id) clock.now returns now - val result = service(Some(dao)).renew(authenticator, Results.Ok) + val result = service(Some(repository)).renew(authenticator, Results.Ok) unserialize(header(settings.headerName, result).get)(settings).get must be equalTo authenticator.copy( id = id, @@ -462,8 +462,8 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch lastUsedDateTime = clock.now ) - there was one(dao).add(any) - there was one(dao).remove(authenticator.id) + there was one(repository).add(any) + there was one(repository).remove(authenticator.id) } "renew the authenticator and return the response with a new JWT if DAO is disabled" in new WithApplication with Context { @@ -481,8 +481,8 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch expirationDateTime = clock.now + settings.authenticatorExpiry, lastUsedDateTime = clock.now ) - there was no(dao).remove(any) - there was no(dao).add(any) + there was no(repository).remove(any) + there was no(repository).add(any) } "throws an AuthenticatorRenewalException exception if an error occurred during renewal" in new Context { @@ -490,12 +490,12 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch val now = new DateTime val id = "new-test-id" - dao.remove(any) returns Future.successful(()) - dao.add(any) returns Future.failed(new Exception("Cannot store authenticator")) + 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 - await(service(Some(dao)).renew(authenticator, Results.Ok)) must throwA[AuthenticatorRenewalException].like { + await(service(Some(repository)).renew(authenticator, Results.Ok)) must throwA[AuthenticatorRenewalException].like { case e => e.getMessage must startWith(RenewError.format(ID, "")) } @@ -506,11 +506,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() - dao.remove(authenticator.id) returns Future.successful(authenticator) + repository.remove(authenticator.id) returns Future.successful(authenticator) - await(service(Some(dao)).discard(authenticator, Results.Ok)) + await(service(Some(repository)).discard(authenticator, Results.Ok)) - there was one(dao).remove(authenticator.id) + there was one(repository).remove(authenticator.id) } "do not remove the authenticator from backing store if DAO is disabled" in new Context { @@ -518,16 +518,16 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch await(service(None).discard(authenticator, Results.Ok)) - there was no(dao).remove(authenticator.id) + there was no(repository).remove(authenticator.id) } "throws an AuthenticatorDiscardingException exception if an error occurred during discarding" in new Context { implicit val request = FakeRequest() val okResult = Results.Ok - dao.remove(authenticator.id) returns Future.failed(new Exception("Cannot remove authenticator")) + repository.remove(authenticator.id) returns Future.failed(new Exception("Cannot remove authenticator")) - await(service(Some(dao)).discard(authenticator, okResult)) must throwA[AuthenticatorDiscardingException].like { + await(service(Some(repository)).discard(authenticator, okResult)) must throwA[AuthenticatorDiscardingException].like { case e => e.getMessage must startWith(DiscardError.format(ID, "")) } @@ -540,9 +540,9 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch trait Context extends Scope { /** - * The backing store implementation. + * The repository implementation. */ - lazy val dao: AuthenticatorDAO[JWTAuthenticator] = mock[AuthenticatorDAO[JWTAuthenticator]] + lazy val repository: AuthenticatorRepository[JWTAuthenticator] = mock[AuthenticatorRepository[JWTAuthenticator]] /** * The ID generator implementation. @@ -569,8 +569,8 @@ class JWTAuthenticatorSpec extends PlaySpecification with Mockito with JsonMatch /** * The authenticator service instance to test. */ - lazy val service = (dao: Option[AuthenticatorDAO[JWTAuthenticator]]) => - new JWTAuthenticatorService(settings, dao, idGenerator, clock) + lazy val service = (repository: Option[AuthenticatorRepository[JWTAuthenticator]]) => + new JWTAuthenticatorService(settings, repository, idGenerator, clock) /** * The login info.