├── .github ├── mergify.yml ├── scala-steward.conf ├── workflows │ ├── publish.yml │ ├── release-drafter.yml │ ├── dependency-graph.yml │ └── build-test.yml ├── dependabot.yml ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── silhouette-cas ├── project │ └── build.properties └── src │ └── main │ └── scala │ └── play │ └── silhouette │ └── impl │ └── providers │ └── CasException.scala ├── project ├── build.properties ├── plugins.sbt └── Dependencies.scala ├── silhouette ├── test │ ├── resources │ │ └── providers │ │ │ ├── oauth2 │ │ │ ├── gitlab.error.json │ │ │ ├── dropbox.error.json │ │ │ ├── foursquare.access.token.json │ │ │ ├── linkedin.access.token.json │ │ │ ├── github.error.json │ │ │ ├── dropbox.access.token.json │ │ │ ├── github.access.token.json │ │ │ ├── gitlab.access.token.json │ │ │ ├── google.access.token.json │ │ │ ├── facebook.access.token.json │ │ │ ├── vk.access.token.json │ │ │ ├── instagram.error.json │ │ │ ├── vk.success.without.photo.json │ │ │ ├── foursquare.error.json │ │ │ ├── github.success.json │ │ │ ├── facebook.error.json │ │ │ ├── dropbox.success.json │ │ │ ├── vk.success.json │ │ │ ├── vk.success.deprecated.json │ │ │ ├── instagram.success.json │ │ │ ├── linkedin.email.json │ │ │ ├── instagram.access.token.json │ │ │ ├── google.error.json │ │ │ ├── facebook.success.json │ │ │ ├── foursquare.success.json │ │ │ ├── foursquare.deprecated.json │ │ │ ├── vk.error.json │ │ │ ├── linkedin.success.json │ │ │ ├── google.error.api.missing.json │ │ │ ├── google.without.email.json │ │ │ ├── gitlab.success.json │ │ │ ├── google.success.json │ │ │ ├── google.img.non-default.json │ │ │ └── google.img.default.json │ │ │ ├── custom │ │ │ ├── auth0.error.json │ │ │ ├── facebook.error.json │ │ │ ├── auth0.success.json │ │ │ ├── facebook.success.json │ │ │ └── auth0.profile.json │ │ │ └── oauth1 │ │ │ ├── twitter.error.json │ │ │ ├── xing.error.json │ │ │ ├── linkedin.error.json │ │ │ ├── linkedin.success.json │ │ │ ├── xing.success.json │ │ │ ├── twitter.success.json │ │ │ └── twitter.with.email.json │ └── play │ │ └── silhouette │ │ ├── api │ │ ├── util │ │ │ ├── ClockSpec.scala │ │ │ ├── PlayHTTPLayerSpec.scala │ │ │ ├── ValueParserSpec.scala │ │ │ └── JsonFormatsSpec.scala │ │ ├── crypto │ │ │ ├── HashSpec.scala │ │ │ └── Base64Spec.scala │ │ ├── AuthenticatorSpec.scala │ │ └── services │ │ │ └── AuthenticatorResultSpec.scala │ │ └── impl │ │ ├── providers │ │ ├── openid │ │ │ ├── service │ │ │ │ └── PlayOpenIDServiceSpec.scala │ │ │ ├── SteamProviderSpec.scala │ │ │ └── YahooProviderSpec.scala │ │ ├── PasswordProviderSpec.scala │ │ ├── SocialProviderRegistrySpec.scala │ │ └── state │ │ │ └── UserStateItemHandlerSpec.scala │ │ └── util │ │ ├── SecureRandomIDGeneratorSpec.scala │ │ ├── PlayCacheLayerSpec.scala │ │ └── DefaultFingerprintGeneratorSpec.scala ├── conf │ ├── messages │ └── reference.conf ├── app │ └── play │ │ └── silhouette │ │ ├── helpers │ │ └── Transform.scala │ │ ├── package.scala │ │ ├── impl │ │ ├── package.scala │ │ ├── util │ │ │ ├── package.scala │ │ │ ├── PlayCacheLayer.scala │ │ │ ├── SecureRandomIDGenerator.scala │ │ │ └── DefaultFingerprintGenerator.scala │ │ ├── services │ │ │ └── package.scala │ │ ├── authenticators │ │ │ └── package.scala │ │ ├── exceptions │ │ │ ├── package.scala │ │ │ ├── OAuth2StateException.scala │ │ │ ├── AccessDeniedException.scala │ │ │ ├── IdentityNotFoundException.scala │ │ │ ├── OAuth1TokenSecretException.scala │ │ │ ├── InvalidPasswordException.scala │ │ │ ├── UnexpectedResponseException.scala │ │ │ └── ProfileRetrievalException.scala │ │ ├── providers │ │ │ ├── package.scala │ │ │ └── openid │ │ │ │ ├── services │ │ │ │ └── PlayOpenIDService.scala │ │ │ │ ├── SteamProvider.scala │ │ │ │ └── YahooProvider.scala │ │ └── User.scala │ │ └── api │ │ ├── AuthInfo.scala │ │ ├── util │ │ ├── package.scala │ │ ├── Credentials.scala │ │ ├── ExecutionContextProvider.scala │ │ ├── FingerprintGenerator.scala │ │ ├── JsonFormats.scala │ │ ├── IDGenerator.scala │ │ ├── Clock.scala │ │ ├── ValueParser.scala │ │ ├── CacheLayer.scala │ │ └── HTTPLayer.scala │ │ ├── exceptions │ │ ├── package.scala │ │ ├── SilhouetteException.scala │ │ ├── AuthenticatorException.scala │ │ ├── CryptoException.scala │ │ ├── ProviderException.scala │ │ ├── ConfigurationException.scala │ │ ├── NotAuthenticatedException.scala │ │ ├── NotAuthorizedException.scala │ │ ├── AuthenticatorRenewalException.scala │ │ ├── AuthenticatorUpdateException.scala │ │ ├── AuthenticatorCreationException.scala │ │ ├── AuthenticatorRetrievalException.scala │ │ ├── AuthenticatorDiscardingException.scala │ │ └── AuthenticatorInitializationException.scala │ │ ├── package.scala │ │ ├── repositories │ │ ├── package.scala │ │ ├── AuthenticatorRepository.scala │ │ └── AuthInfoRepository.scala │ │ ├── services │ │ ├── package.scala │ │ ├── AvatarService.scala │ │ └── IdentityService.scala │ │ ├── Logger.scala │ │ ├── Identity.scala │ │ ├── crypto │ │ ├── Signer.scala │ │ ├── Crypter.scala │ │ ├── Base64.scala │ │ ├── AuthenticatorEncoder.scala │ │ └── Hash.scala │ │ ├── LoginInfo.scala │ │ ├── Provider.scala │ │ └── Silhouette.scala └── app-2 │ └── play │ └── silhouette │ └── api │ └── Environment.scala ├── .gitignore ├── .editorconfig ├── silhouette-persistence └── src │ ├── main │ └── scala │ │ └── play │ │ └── silhouette │ │ └── persistence │ │ ├── daos │ │ ├── package.scala │ │ ├── DelegableAuthInfoDAO.scala │ │ ├── AuthInfoDAO.scala │ │ └── InMemoryAuthInfoDAO.scala │ │ └── repositories │ │ ├── package.scala │ │ └── CacheAuthenticatorRepository.scala │ └── test │ └── scala │ └── play │ └── silhouette │ ├── test │ └── WaitPatience.scala │ └── persistence │ ├── repositories │ └── CacheAuthenticatorRepositorySpec.scala │ └── daos │ └── InMemoryAuthInfoDAOSpec.scala ├── silhouette-crypto-jca └── src │ └── test │ └── scala │ └── play │ └── silhouette │ └── crypto │ ├── JcaCrypterSpec.scala │ └── JcaSignerSpec.scala ├── silhouette-testkit ├── app-2 │ └── play │ │ └── silhouette │ │ └── test │ │ └── package.scala └── app-3 │ └── play │ └── silhouette │ └── test │ └── package.scala ├── CONTRIBUTING.md └── silhouette-password-bcrypt └── src └── main └── scala └── play └── silhouette └── password └── BCryptSha256PasswordHasher.scala /.github/mergify.yml: -------------------------------------------------------------------------------- 1 | extends: .github 2 | -------------------------------------------------------------------------------- /silhouette-cas/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.7 2 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | # suppress inspection "UnusedProperty" 2 | sbt.version = 1.11.7 3 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/gitlab.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "Bad credentials" 3 | } 4 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/dropbox.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": "Invalid OAuth request." 3 | } 4 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/foursquare.access.token.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token": "my.access.token" 3 | } 4 | -------------------------------------------------------------------------------- /silhouette/conf/messages: -------------------------------------------------------------------------------- 1 | silhouette.not.authenticated = Authentication required 2 | silhouette.not.authorized = Not authorized 3 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/linkedin.access.token.json: -------------------------------------------------------------------------------- 1 | { 2 | "expires_in": 5184000, 3 | "access_token": "my.access.token" 4 | } 5 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/custom/auth0.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": "invalid_request", 3 | "error_description": "the connection was disabled" 4 | } 5 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/github.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "Bad credentials", 3 | "documentation_url": "http://developer.github.com/v3" 4 | } 5 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/dropbox.access.token.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token": "my.access.token", 3 | "token_type": "bearer", 4 | "uid": "12345" 5 | } 6 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/github.access.token.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token": "my.access.token", 3 | "scope": "repo,gist", 4 | "token_type": "bearer" 5 | } 6 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/gitlab.access.token.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token": "my.access.token", 3 | "token_type": "bearer", 4 | "expires_in": 7200 5 | } 6 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/google.access.token.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token": "my.access.token", 3 | "expires_in": 5184000, 4 | "token_type": "Bearer" 5 | } 6 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/facebook.access.token.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token": "my.access.token", 3 | "token_type": "bearer", 4 | "expires_in": 5183836 5 | } 6 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth1/twitter.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "message": "Bad Authentication data", 5 | "code": 215 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth1/xing.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error_name": "INVALID_PARAMETERS", 3 | "message": "Invalid parameters (Limit must be a non-negative number.)" 4 | } 5 | -------------------------------------------------------------------------------- /silhouette-cas/src/main/scala/play/silhouette/impl/providers/CasException.scala: -------------------------------------------------------------------------------- 1 | package play.silhouette.impl.providers 2 | 3 | class CasException(message: String) extends Exception(message) 4 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/vk.access.token.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token": "my.access.token", 3 | "expires_in": 43200, 4 | "user_id": 6492, 5 | "email": "apollonia.vanova@watchmen.com" 6 | } 7 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth1/linkedin.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "errorCode": 0, 3 | "message": "Unknown authentication scheme", 4 | "requestId": "LY860UAC5U", 5 | "status": 401, 6 | "timestamp": 1390421660154 7 | } 8 | -------------------------------------------------------------------------------- /.github/scala-steward.conf: -------------------------------------------------------------------------------- 1 | commits.message = "${artifactName} ${nextVersion} (was ${currentVersion})" 2 | 3 | pullRequests.grouping = [ 4 | { name = "patches", "title" = "Patch updates", "filter" = [{"version" = "patch"}] } 5 | ] 6 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/instagram.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "error_type": "OAuthAccessTokenException", 4 | "code": 400, 5 | "error_message": "The access_token provided is invalid." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/vk.success.without.photo.json: -------------------------------------------------------------------------------- 1 | { 2 | "response": [ 3 | { 4 | "id": 66748, 5 | "first_name": "Apollonia", 6 | "last_name": "Vanova" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/foursquare.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "code": 400, 4 | "errorType": "param_error", 5 | "errorDetail": "Must provide a valid user ID or 'self.'" 6 | }, 7 | "response": { } 8 | } 9 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/github.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "avatar_url": "https://github.com/images/error/apollonia_vanova.gif", 4 | "name": "Apollonia Vanova", 5 | "email": "apollonia.vanova@watchmen.com" 6 | } 7 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/custom/facebook.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": { 3 | "message": "An active access token must be used to query information about the current user.", 4 | "type": "OAuthException", 5 | "code": 2500 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/facebook.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": { 3 | "message": "An active access token must be used to query information about the current user.", 4 | "type": "OAuthException", 5 | "code": 2500 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/dropbox.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "uid": 12345678, 3 | "display_name": "Apollonia Vanova", 4 | "name_details": { 5 | "familiar_name": "Apollonia", 6 | "given_name": "Apollonia", 7 | "surname": "Vanova" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/vk.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "response": [ 3 | { 4 | "id": 66748, 5 | "first_name": "Apollonia", 6 | "last_name": "Vanova", 7 | "photo_max_orig": "http://vk.com/images/camera_b.gif" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/vk.success.deprecated.json: -------------------------------------------------------------------------------- 1 | { 2 | "response": [ 3 | { 4 | "uid": 66748, 5 | "first_name": "Apollonia", 6 | "last_name": "Vanova", 7 | "photo": "http://vk.com/images/camera_b.gif" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/helpers/Transform.scala: -------------------------------------------------------------------------------- 1 | package play.silhouette.helpers 2 | 3 | private[silhouette] object Transform { 4 | 5 | implicit class MapOps[K, V](val map: Map[K, V]) extends AnyVal { 6 | def transformValues[W](f: V => W): Map[K, W] = map.view.mapValues(f).toMap 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/package.scala: -------------------------------------------------------------------------------- 1 | package play 2 | 3 | /** 4 | * An authentication library for Play Framework applications that supports several authentication methods, 5 | * including OAuth1, OAuth2, OpenID, Credentials or custom authentication schemes. 6 | */ 7 | package object silhouette 8 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin(dependency = "ch.epfl.scala" % "sbt-version-policy" % "3.2.1") 2 | addSbtPlugin(dependency = "com.github.sbt" % "sbt-unidoc" % "0.6.0") 3 | addSbtPlugin(dependency = "org.playframework" % "sbt-plugin" % "3.1.0-M4") 4 | addSbtPlugin(dependency = "com.github.sbt" % "sbt-ci-release" % "1.11.2") 5 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/instagram.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "code": 200 4 | }, 5 | "data": { 6 | "id": "1574083", 7 | "full_name": "Apollonia Vanova", 8 | "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1574083_75sq_1295469061.jpg" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/linkedin.email.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements": [ 3 | { 4 | "handle": "urn:li:emailAddress:30919315", 5 | "type": "EMAIL", 6 | "handle~": { 7 | "emailAddress": "apollonia.vanova@watchmen.com" 8 | }, 9 | "primary": true 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/project 3 | target 4 | tmp 5 | dist 6 | /.idea 7 | /*.iml 8 | /out 9 | /.idea_modules 10 | /.classpath 11 | /.project 12 | /RUNNING_PID 13 | /.settings 14 | /.target 15 | /.cache 16 | *.DS_Store 17 | 18 | # Metals 19 | .metals/* 20 | .bloop/* 21 | .vscode/* 22 | **/.bloop/* 23 | **/metals.sbt 24 | 25 | .bsp/* 26 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | indent_style = space 9 | indent_size = 2 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: # Snapshots 6 | - main 7 | tags: ["**"] # Releases 8 | 9 | jobs: 10 | publish-artifacts: 11 | name: Publish / Artifacts 12 | uses: playframework/.github/.github/workflows/publish.yml@v4 13 | with: 14 | java: 17 15 | secrets: inherit 16 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/instagram.access.token.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token": "my.access.token", 3 | "user": { 4 | "id": "1574083", 5 | "username": "apollonia.vanova", 6 | "full_name": "Apollonia Vanova", 7 | "profile_picture": "http://distillery.s3.amazonaws.com/profiles/profile_1574083_75sq_1295469061.jpg" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth1/linkedin.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "NhZXBl_O6f", 3 | "firstName": "Apollonia", 4 | "lastName": "Vanova", 5 | "pictureUrl": "http://media.linkedin.com/mpr/mprx/0_fsPnURNRhLhk_Ue2fjKLUZkB2FL6TOe2S4bdUZz61GA9Ysxu_y_sz4THGW5JGJWhaMleN0F61-Dg", 6 | "formattedName": "Apollonia Vanova", 7 | "emailAddress": "apollonia.vanova@watchmen.com" 8 | } 9 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/google.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": { 3 | "errors": [ 4 | { 5 | "domain": "global", 6 | "reason": "authError", 7 | "message": "Invalid Credentials", 8 | "locationType": "header", 9 | "location": "Authorization" 10 | } 11 | ], 12 | "code": 401, 13 | "message": "Invalid Credentials" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/facebook.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "134405962728980", 3 | "name": "Apollonia Vanova", 4 | "first_name": "Apollonia", 5 | "last_name": "Vanova", 6 | "email": "apollonia.vanova@watchmen.com", 7 | "picture": { 8 | "data": { 9 | "url": "https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-ash2/t1/36245_155530314499277_2350717_n.jpg?lvh=1" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/custom/auth0.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2NtLXRlc3QuZXUuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDU2YmIwODVmZGE1OTU4NjQwMjcyZmI5YSIsImF1ZCI6Im15SDM2eDZyMTh0TEhxRGxjR21FbGJTd1paZGdjUXpoIiwiZXhwIjoxNDU1MTMzOTkxLCJpYXQiOjE0NTUwOTc5OTF9.hH5Bim8NpVwnw0hEZ3psoq4pjrwbVBgXXh7udcvL8os", 3 | "access_token": "uytvrlJcwrGtvejb", 4 | "token_type": "bearer" 5 | } 6 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/custom/facebook.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "134405962728980", 3 | "name": "Apollonia Vanova", 4 | "first_name": "Apollonia", 5 | "last_name": "Vanova", 6 | "email": "apollonia.vanova@watchmen.com", 7 | "gender": "male", 8 | "picture": { 9 | "data": { 10 | "url": "https://fbcdn-sphotos-g-a.akamaihd.net/hphotos-ak-ash2/t1/36245_155530314499277_2350717_n.jpg?lvh=1" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth1/xing.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "users": [ 3 | { 4 | "id": "1235468792", 5 | "first_name": "Apollonia", 6 | "last_name": "Vanova", 7 | "display_name": "Apollonia Vanova", 8 | "active_email": "apollonia.vanova@watchmen.com", 9 | "photo_urls": { 10 | "large": "http://www.xing.com/img/users/e/3/d/f94ef165a.123456,1.140x185.jpg" 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth1/twitter.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 6253282, 3 | "id_str": "6253282", 4 | "name": "Apollonia Vanova", 5 | "screen_name": "apolloniavanova", 6 | "location": "Slovak", 7 | "description": "A Slovakian-born actress, known for her roles as Silhouette in the film version of Watchmen.", 8 | "url": "http:\/\/apolloniavanova.com", 9 | "profile_image_url_https": "https://pbs.twimg.com/profile_images/1209905677/appolonia_.jpg" 10 | } 11 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/foursquare.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "code": 200 4 | }, 5 | "response": { 6 | "user": { 7 | "id": "13221052", 8 | "firstName": "Apollonia", 9 | "lastName": "Vanova", 10 | "photo": { 11 | "prefix": "https://irs0.4sqi.net/img/user/", 12 | "suffix": "/blank_girl.png" 13 | }, 14 | "contact": { 15 | "email": "apollonia.vanova@watchmen.com" 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth1/twitter.with.email.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 6253282, 3 | "id_str": "6253282", 4 | "name": "Apollonia Vanova", 5 | "email": "apollonia.vanova@watchmen.com", 6 | "screen_name": "apolloniavanova", 7 | "location": "Slovak", 8 | "description": "A Slovakian-born actress, known for her roles as Silhouette in the film version of Watchmen.", 9 | "url": "http:\/\/apolloniavanova.com", 10 | "profile_image_url_https": "https://pbs.twimg.com/profile_images/1209905677/appolonia_.jpg" 11 | } 12 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/foursquare.deprecated.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "code": 200, 4 | "errorType": "deprecated" 5 | }, 6 | "response": { 7 | "user": { 8 | "id": "13221052", 9 | "firstName": "Apollonia", 10 | "lastName": "Vanova", 11 | "photo": { 12 | "prefix": "https://irs0.4sqi.net/img/user/", 13 | "suffix": "/blank_girl.png" 14 | }, 15 | "contact": { 16 | "email": "apollonia.vanova@watchmen.com" 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | target-branch: "10.x" 12 | commit-message: 13 | prefix: "[10.x] " 14 | - package-ecosystem: "github-actions" 15 | directory: "/" 16 | schedule: 17 | interval: "weekly" 18 | target-branch: "9.x" 19 | commit-message: 20 | prefix: "[9.x] " 21 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/vk.error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": { 3 | "error_code": 10, 4 | "error_msg": "Internal server error: could not get application", 5 | "request_params": [ 6 | { 7 | "key": "oauth", 8 | "value": "1" 9 | }, 10 | { 11 | "key": "method", 12 | "value": "getProfiles" 13 | }, 14 | { 15 | "key": "fields", 16 | "value": "uid,first_name,last_name,photo" 17 | }, 18 | { 19 | "key": "access_token", 20 | "value":"test.token" 21 | } 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | update_release_draft: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: release-drafter/release-drafter@v6 13 | with: 14 | name: "Play Silhouette $RESOLVED_VERSION" 15 | config-name: release-drafts/increasing-minor-version.yml # located in .github/ in the default branch within this or the .github repo 16 | commitish: ${{ github.ref_name }} 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/linkedin.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "NhZXBl_O6f", 3 | "localizedFirstName": "Apollonia", 4 | "localizedLastName": "Vanova", 5 | "lastName": { 6 | "localized": { 7 | "en_US": "Vanova" 8 | }, 9 | "preferredLocale": { 10 | "country": "US", 11 | "language": "en" 12 | } 13 | }, 14 | "firstName": { 15 | "localized": { 16 | "en_US": "Apollonia" 17 | }, 18 | "preferredLocale": { 19 | "country": "US", 20 | "language": "en" 21 | } 22 | }, 23 | "profilePicture": { 24 | "displayImage": "urn:li:digitalmediaAsset:Ue2fjKLUZkB2FL6TOe2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/dependency-graph.yml: -------------------------------------------------------------------------------- 1 | name: Dependency Graph 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | concurrency: 8 | # Only run once for latest commit per ref and cancel other (previous) runs. 9 | group: dependency-graph-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | permissions: 13 | contents: write # this permission is needed to submit the dependency graph 14 | 15 | jobs: 16 | dependency-graph: 17 | name: Submit dependencies to GitHub 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v6 21 | with: 22 | fetch-depth: 0 23 | ref: ${{ inputs.ref }} 24 | - uses: sbt/setup-sbt@v1 25 | - uses: scalacenter/sbt-dependency-submission@v3 26 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Pull Request Checklist 2 | 3 | * [ ] Have you read [How to write the perfect pull request](https://github.com/blog/1943-how-to-write-the-perfect-pull-request)? 4 | * [ ] Have you read through the [contributor guidelines](https://github.com/playframework/play-silhouette/blob/main/CONTRIBUTING.md)? 5 | * [ ] Have you added copyright headers to new files? 6 | * [ ] Have you suggest documentation edits? 7 | * [ ] Have you added tests for any changed functionality? 8 | 9 | ## Fixes 10 | 11 | Fixes #xxxx 12 | 13 | ## Purpose 14 | 15 | What does this PR do? 16 | 17 | ## Background Context 18 | 19 | Why did you take this approach? 20 | 21 | ## References 22 | 23 | Are there any relevant issues / PRs / mailing lists discussions? 24 | -------------------------------------------------------------------------------- /silhouette/conf/reference.conf: -------------------------------------------------------------------------------- 1 | play.i18n.langs = [ "en", "en-US" ] 2 | 3 | # Secret key 4 | # ~~~~~ 5 | # The secret key is used to secure cryptographics functions. 6 | # If you deploy your application to several instances be sure to use the same key! 7 | play.http.secret.key="1s`20s2deE$r;Io]w^fpx:x^HEQ9eyeF7H:MS10iGOxdh1GH5p/hDw=Sq[ieK]Ndwfg" 8 | 9 | # The application DI modules 10 | # ~~~~~ 11 | play.modules.enabled += "play.silhouette.api.actions.SecuredActionModule" 12 | play.modules.enabled += "play.silhouette.api.actions.SecuredErrorHandlerModule" 13 | play.modules.enabled += "play.silhouette.api.actions.UnsecuredActionModule" 14 | play.modules.enabled += "play.silhouette.api.actions.UnsecuredErrorHandlerModule" 15 | play.modules.enabled += "play.silhouette.api.actions.UserAwareActionModule" 16 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/custom/auth0.profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "john@company.org", 3 | "email_verified": true, 4 | "clientID": "tSWS3Y1pFaFbY4v3hPwYa46TepGtg875", 5 | "updated_at": "2016-02-10T11:05:23.689Z", 6 | "picture": "https://s.gravatar.com/avatar/c1c49231ce863e5f33d7f42cd44632f4?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Flu.png", 7 | "user_id": "auth0|56961100fc02d8a0339b1a2a", 8 | "name": "john@company.org", 9 | "nickname": "john", 10 | "identities": [ 11 | { 12 | "user_id": "56961100fc02d8a0339b1a2a", 13 | "provider": "auth0", 14 | "connection": "Username-Password-Authentication", 15 | "isSocial": false 16 | } 17 | ], 18 | "created_at": "2016-01-13T08:55:28.389Z", 19 | "last_password_reset": "2016-02-08T11:14:20.663Z", 20 | "sub": "auth0|56961100fc02d8a0339b1a2a" 21 | } -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette 17 | 18 | /** 19 | * The reference implementation of Silhouette. 20 | */ 21 | package object impl 22 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/AuthInfo.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api 17 | 18 | /** 19 | * A marker trait for authentication information. 20 | */ 21 | trait AuthInfo 22 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/util/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api 17 | 18 | /** 19 | * Provides utilities used by the API. 20 | */ 21 | package object util {} 22 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api 17 | 18 | /** 19 | * Provides exceptions used by the API. 20 | */ 21 | package object exceptions 22 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/util/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl 17 | 18 | /** 19 | * Provides implementations of utility traits. 20 | */ 21 | package object util {} 22 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/services/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl 17 | 18 | /** 19 | * Provides implementations of the services. 20 | */ 21 | package object services {} 22 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/google.error.api.missing.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": { 3 | "code": 403, 4 | "message": "People API has not been used in project 1234567890 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/people.googleapis.com/overview?project=1234567890 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.", 5 | "status": "PERMISSION_DENIED", 6 | "details": [ 7 | { 8 | "@type": "type.googleapis.com/google.rpc.Help", 9 | "links": [ 10 | { 11 | "description": "Google developers console API activation", 12 | "url": "https://console.developers.google.com/apis/api/people.googleapis.com/overview?project=1234567890" 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/authenticators/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl 17 | 18 | /** 19 | * Reference implementations of the authenticators. 20 | */ 21 | package object authenticators 22 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette 17 | 18 | /** 19 | * The collection of traits and utility classes that form the stable API of Silhouette. 20 | */ 21 | package object api 22 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/exceptions/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl 17 | 18 | /** 19 | * Provides exceptions thrown in the reference implementation. 20 | */ 21 | package object exceptions 22 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/repositories/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api 17 | 18 | /** 19 | * Provides repositories used by the API to persist entities. 20 | */ 21 | package object repositories {} 22 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/services/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api 17 | 18 | /** 19 | * Provides services used by the API to call external or internal services. 20 | */ 21 | package object services {} 22 | -------------------------------------------------------------------------------- /silhouette-persistence/src/main/scala/play/silhouette/persistence/daos/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.persistence 17 | 18 | /** 19 | * Provides DAO implementations to persist and retrieve objects. 20 | */ 21 | package object daos 22 | -------------------------------------------------------------------------------- /silhouette-persistence/src/main/scala/play/silhouette/persistence/repositories/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.persistence 17 | 18 | /** 19 | * Provides implementations of the repositories. 20 | */ 21 | package object repositories {} 22 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/google.without.email.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceName": "people/109476598527568979481", 3 | "etag": "%EggBAj0DCT43LohDUMMYVALUEgbwgJCgsMIgxkaGtKb3cwenMxMD1=", 4 | "names": [ 5 | { 6 | "metadata": { 7 | "primary": true, 8 | "source": { 9 | "type": "PROFILE", 10 | "id": "109476598527568979481" 11 | } 12 | }, 13 | "displayName": "Apollonia Vanova", 14 | "familyName": "Vanova", 15 | "givenName": "Apollonia", 16 | "displayNameLastFirst": "Vanova, Apollonia" 17 | } 18 | ], 19 | "photos": [ 20 | { 21 | "metadata": { 22 | "primary": true, 23 | "source": { 24 | "type": "PROFILE", 25 | "id": "109476598527568979481" 26 | } 27 | }, 28 | "url": "https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50" 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/providers/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl 17 | 18 | /** 19 | * Contains [[play.silhouette.api.Provider]] implementations that provide authentication 20 | * for different schemes and services. 21 | */ 22 | package object providers {} 23 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/Logger.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api 17 | 18 | /** 19 | * Implement this to get a named logger in scope. 20 | */ 21 | trait Logger { 22 | 23 | /** 24 | * A named logger instance. 25 | */ 26 | val logger = play.api.Logger(this.getClass) 27 | } 28 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/SilhouetteException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * A marker exception for the Silhouette project. 20 | */ 21 | class SilhouetteException(msg: String, cause: Option[Throwable] = None) 22 | extends Exception(msg, cause.orNull) 23 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/AuthenticatorException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * An exception for all authenticator related errors. 20 | */ 21 | class AuthenticatorException(msg: String, cause: Option[Throwable] = None) 22 | extends SilhouetteException(msg, cause) 23 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/util/Credentials.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.util 17 | 18 | /** 19 | * Credentials to authenticate with. 20 | * 21 | * @param identifier The unique identifier to authenticate with. 22 | * @param password The password to authenticate with. 23 | */ 24 | final case class Credentials(identifier: String, password: String) 25 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/CryptoException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * Indicates that a crypto operation error occurred. 20 | * 21 | * @param msg The exception message. 22 | * @param cause The exception cause. 23 | */ 24 | class CryptoException(msg: String, cause: Option[Throwable] = None) 25 | extends SilhouetteException(msg, cause) 26 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/ProviderException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * Indicates an error occurred with an authentication provider. 20 | * 21 | * @param msg The exception message. 22 | * @param cause The exception cause. 23 | */ 24 | class ProviderException(msg: String, cause: Option[Throwable] = None) 25 | extends SilhouetteException(msg, cause) 26 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/ConfigurationException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * Indicates a misconfiguration of a Silhouette component. 20 | * 21 | * @param msg The exception message. 22 | * @param cause The exception cause. 23 | */ 24 | class ConfigurationException(msg: String, cause: Option[Throwable] = None) 25 | extends SilhouetteException(msg, cause) 26 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/Identity.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: SecureSocial (https://github.com/jaliss/securesocial) 3 | * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss 4 | * 5 | * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) 6 | * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package play.silhouette.api 21 | 22 | /** 23 | * This trait represents an authenticated user. 24 | */ 25 | trait Identity 26 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/NotAuthenticatedException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * Indicates that a user is not authenticated to access a secured endpoint. 20 | * 21 | * @param msg The exception message. 22 | * @param cause The exception cause. 23 | */ 24 | class NotAuthenticatedException(msg: String, cause: Option[Throwable] = None) 25 | extends SilhouetteException(msg, cause) 26 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/NotAuthorizedException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * Indicates that the authenticated user is not authorized to access a secured endpoint. 20 | * 21 | * @param msg The exception message. 22 | * @param cause The exception cause. 23 | */ 24 | class NotAuthorizedException(msg: String, cause: Option[Throwable] = None) 25 | extends SilhouetteException(msg, cause) 26 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/util/ExecutionContextProvider.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.util 17 | 18 | import scala.concurrent.ExecutionContext 19 | 20 | /** 21 | * A trait that can be mixed in to provide an execution context. 22 | */ 23 | trait ExecutionContextProvider { 24 | 25 | /** 26 | * The execution context to handle the asynchronous operations. 27 | */ 28 | implicit val executionContext: ExecutionContext 29 | } 30 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/AuthenticatorRenewalException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * An exception thrown when there is an error during authenticator renewal. 20 | * 21 | * @param msg The exception message. 22 | * @param cause The exception cause. 23 | */ 24 | class AuthenticatorRenewalException(msg: String, cause: Option[Throwable] = None) 25 | extends AuthenticatorException(msg, cause) 26 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/AuthenticatorUpdateException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * An exception thrown when there is an error during authenticator update. 20 | * 21 | * @param msg The exception message. 22 | * @param cause The exception cause. 23 | */ 24 | class AuthenticatorUpdateException(msg: String, cause: Option[Throwable] = None) 25 | extends AuthenticatorException(msg, cause) 26 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/AuthenticatorCreationException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * An exception thrown when there is an error during authenticator creation. 20 | * 21 | * @param msg The exception message. 22 | * @param cause The exception cause. 23 | */ 24 | class AuthenticatorCreationException(msg: String, cause: Option[Throwable] = None) 25 | extends AuthenticatorException(msg, cause) 26 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/AuthenticatorRetrievalException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * An exception thrown when there is an error during authenticator retrieval. 20 | * 21 | * @param msg The exception message. 22 | * @param cause The exception cause. 23 | */ 24 | class AuthenticatorRetrievalException(msg: String, cause: Option[Throwable] = None) 25 | extends AuthenticatorException(msg, cause) 26 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/AuthenticatorDiscardingException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * An exception thrown when there is an error during authenticator discarding. 20 | * 21 | * @param msg The exception message. 22 | * @param cause The exception cause. 23 | */ 24 | class AuthenticatorDiscardingException(msg: String, cause: Option[Throwable] = None) 25 | extends AuthenticatorException(msg, cause) 26 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/exceptions/AuthenticatorInitializationException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.exceptions 17 | 18 | /** 19 | * An exception thrown when there is an error during authenticator initialization. 20 | * 21 | * @param msg The exception message. 22 | * @param cause The exception cause. 23 | */ 24 | class AuthenticatorInitializationException(msg: String, cause: Option[Throwable] = None) 25 | extends AuthenticatorException(msg, cause) 26 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/exceptions/OAuth2StateException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.exceptions 17 | 18 | import play.silhouette.api.exceptions.ProviderException 19 | 20 | /** 21 | * Indicates that an error occurred during OAuth2 state retrieval. 22 | * 23 | * @param msg The exception message. 24 | * @param cause The exception cause. 25 | */ 26 | class OAuth2StateException(msg: String, cause: Option[Throwable] = None) 27 | extends ProviderException(msg, cause) 28 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/exceptions/AccessDeniedException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.exceptions 17 | 18 | import play.silhouette.api.exceptions.ProviderException 19 | 20 | /** 21 | * Signals that a social provider denies access during authentication process. 22 | * 23 | * @param msg The exception message. 24 | * @param cause The exception cause. 25 | */ 26 | class AccessDeniedException(msg: String, cause: Option[Throwable] = None) 27 | extends ProviderException(msg, cause) 28 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/exceptions/IdentityNotFoundException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.exceptions 17 | 18 | import play.silhouette.api.exceptions.ProviderException 19 | 20 | /** 21 | * Signals that an identity could not found in a credential based provider. 22 | * 23 | * @param msg The exception message. 24 | * @param cause The exception cause. 25 | */ 26 | class IdentityNotFoundException(msg: String, cause: Option[Throwable] = None) 27 | extends ProviderException(msg, cause) 28 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/exceptions/OAuth1TokenSecretException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.exceptions 17 | 18 | import play.silhouette.api.exceptions.ProviderException 19 | 20 | /** 21 | * Indicates that an error occurred during OAuth1 token secret retrieval. 22 | * 23 | * @param msg The exception message. 24 | * @param cause The exception cause. 25 | */ 26 | class OAuth1TokenSecretException(msg: String, cause: Option[Throwable] = None) 27 | extends ProviderException(msg, cause) 28 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/exceptions/InvalidPasswordException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.exceptions 17 | 18 | import play.silhouette.api.exceptions.ProviderException 19 | 20 | /** 21 | * Indicates that an invalid password was entered in a credential based provider. 22 | * 23 | * @param msg The exception message. 24 | * @param cause The exception cause. 25 | */ 26 | class InvalidPasswordException(msg: String, cause: Option[Throwable] = None) 27 | extends ProviderException(msg, cause) 28 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/exceptions/UnexpectedResponseException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.exceptions 17 | 18 | import play.silhouette.api.exceptions.ProviderException 19 | 20 | /** 21 | * Signals that an unexpected response was received from a social provider. 22 | * 23 | * @param msg The exception message. 24 | * @param cause The exception cause. 25 | */ 26 | class UnexpectedResponseException(msg: String, cause: Option[Throwable] = None) 27 | extends ProviderException(msg, cause) 28 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/exceptions/ProfileRetrievalException.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.exceptions 17 | 18 | import play.silhouette.api.exceptions.ProviderException 19 | 20 | /** 21 | * Indicates that an error occurred during profile retrieval in a social provider. 22 | * 23 | * @param msg The exception message. 24 | * @param cause The exception cause. 25 | */ 26 | class ProfileRetrievalException(msg: String, cause: Option[Throwable] = None) 27 | extends ProviderException(msg, cause) 28 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/gitlab.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "username": "john_smith", 4 | "email": "john@example.com", 5 | "name": "John Smith", 6 | "state": "active", 7 | "avatar_url": "http://gitlab.com/uploads/user/avatar/1/index.jpg", 8 | "web_url": "http://gitlab.com/u/john_smith", 9 | "created_at": "2012-05-23T08:00:58Z", 10 | "is_admin": false, 11 | "bio": null, 12 | "location": null, 13 | "skype": "", 14 | "linkedin": "", 15 | "twitter": "", 16 | "website_url": "", 17 | "last_sign_in_at": "2012-06-01T11:41:01Z", 18 | "confirmed_at": "2012-05-23T09:05:22Z", 19 | "theme_id": 1, 20 | "color_scheme_id": 2, 21 | "projects_limit": 100, 22 | "current_sign_in_at": "2012-06-02T06:36:55Z", 23 | "identities": [ 24 | { 25 | "provider": "github", 26 | "extern_uid": "2435223452345" 27 | }, 28 | { 29 | "provider": "bitbucket", 30 | "extern_uid": "john_smith" 31 | }, 32 | { 33 | "provider": "google_oauth2", 34 | "extern_uid": "8776128412476123468721346" 35 | } 36 | ], 37 | "can_create_group": true, 38 | "can_create_project": true, 39 | "two_factor_enabled": true, 40 | "external": false, 41 | "private_token": "dd34asd13as" 42 | } 43 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/api/util/ClockSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.util 17 | 18 | import play.api.test._ 19 | 20 | import java.time.ZonedDateTime 21 | 22 | /** 23 | * Test case for the [[play.silhouette.api.util.Clock]] class. 24 | */ 25 | class ClockSpec extends PlaySpecification { 26 | 27 | "The `apply` method" should { 28 | "return a new Clock instance" in { 29 | Clock() should beAnInstanceOf[Clock] 30 | } 31 | } 32 | 33 | "The `now` method" should { 34 | "return a new DateTime instance" in { 35 | Clock().now should beAnInstanceOf[ZonedDateTime] 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/crypto/Signer.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.crypto 17 | 18 | import scala.util.Try 19 | 20 | /** 21 | * Specifies a strategy how data can be signed. 22 | */ 23 | trait Signer { 24 | 25 | /** 26 | * Signs the given data using the given secret key. 27 | * 28 | * @param data The data to sign. 29 | * @return A message authentication code. 30 | */ 31 | def sign(data: String): String 32 | 33 | /** 34 | * Extracts a message that was signed by [[Signer.sign]]. 35 | * 36 | * @param message The signed message to extract. 37 | * @return The verified raw data, or an error if the message isn't valid. 38 | */ 39 | def extract(message: String): Try[String] 40 | } 41 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/api/crypto/HashSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.crypto 17 | 18 | import play.api.test._ 19 | 20 | /** 21 | * Test case for the [[play.silhouette.api.crypto.Hash]] object. 22 | */ 23 | class HashSpec extends PlaySpecification { 24 | 25 | "The `sha1` method" should { 26 | "create a SHA1 hash of a string" in { 27 | Hash.sha1("SÄÜ%&/($§QW@\\'Ä_:;>|§`´*~") must be equalTo "a87babacb5ef14f1f811527c2028706a55c56be5" 28 | } 29 | } 30 | 31 | "The `sha2` method" should { 32 | "create a SHA2 hash of a string" in { 33 | Hash.sha2("SÄÜ%&/($§QW@\\'Ä_:;>|§`´*~") must be equalTo "162b62e492f8f4d979d5f96c1fb96c7bf5c0621f48f0613bf16fa527e41c54e5" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/util/FingerprintGenerator.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: SecureSocial (https://github.com/jaliss/securesocial) 3 | * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss 4 | * 5 | * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) 6 | * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package play.silhouette.api.util 21 | 22 | import play.api.mvc.RequestHeader 23 | 24 | /** 25 | * A generator which creates a fingerprint to identify a user. 26 | */ 27 | trait FingerprintGenerator { 28 | 29 | /** 30 | * Generates a fingerprint from request. 31 | * 32 | * @param request The request header. 33 | * @return The generated fingerprint. 34 | */ 35 | def generate(implicit request: RequestHeader): String 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/build-test.yml: -------------------------------------------------------------------------------- 1 | name: Check 2 | 3 | on: 4 | pull_request: 5 | 6 | push: 7 | branches: 8 | - main # Check branch after merge 9 | 10 | concurrency: 11 | # Only run once for latest commit per ref and cancel other (previous) runs. 12 | group: ci-${{ github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | # check-code-style: 17 | # name: Code Style 18 | # uses: playframework/.github/.github/workflows/cmd.yml@v4 19 | # with: 20 | # cmd: sbt validateCode 21 | 22 | check-binary-compatibility: 23 | name: Binary Compatibility 24 | uses: playframework/.github/.github/workflows/binary-check.yml@v4 25 | with: 26 | java: 17 27 | 28 | check-docs: 29 | name: Docs 30 | uses: playframework/.github/.github/workflows/cmd.yml@v4 31 | with: 32 | java: 21, 17 33 | scala: 2.13.x, 3.x 34 | cmd: sbt ++$MATRIX_SCALA doc 35 | 36 | tests: 37 | name: Tests 38 | needs: 39 | #- "check-code-style" 40 | - "check-binary-compatibility" 41 | - "check-docs" 42 | uses: playframework/.github/.github/workflows/cmd.yml@v4 43 | with: 44 | java: 21, 17 45 | scala: 2.13.x, 3.x 46 | cmd: >- 47 | sbt ++$MATRIX_SCALA test 48 | 49 | finish: 50 | name: Finish 51 | if: github.event_name == 'pull_request' 52 | needs: # Should be last 53 | - "tests" 54 | uses: playframework/.github/.github/workflows/rtm.yml@v4 55 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/util/JsonFormats.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.util 17 | 18 | import play.api.libs.json._ 19 | import play.api.libs.json.Reads._ 20 | import play.api.libs.json.Writes._ 21 | 22 | import scala.concurrent.duration._ 23 | 24 | /** 25 | * Some implicit Json formats. 26 | */ 27 | object JsonFormats { 28 | 29 | /** 30 | * Converts FiniteDuration object to JSON and vice versa. 31 | * 32 | * We use seconds here because it the smallest unit used by Silhouette. 33 | */ 34 | implicit object FiniteDurationFormat extends Format[FiniteDuration] { 35 | def reads(json: JsValue): JsResult[FiniteDuration] = LongReads.reads(json).map(_.seconds) 36 | def writes(o: FiniteDuration): JsValue = LongWrites.writes(o.toSeconds) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/api/crypto/Base64Spec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.crypto 17 | 18 | import play.api.libs.json.Json 19 | import play.api.test._ 20 | 21 | /** 22 | * Test case for the [[Base64]] object. 23 | */ 24 | class Base64Spec extends PlaySpecification { 25 | 26 | "The `decode` method" should { 27 | "decode a Base64 string" in { 28 | Base64.decode("SGVsbG8gV29ybGQh") must be equalTo "Hello World!" 29 | } 30 | } 31 | 32 | "The `encode` method" should { 33 | "encode a string as Base64" in { 34 | Base64.encode("Hello World!") must be equalTo "SGVsbG8gV29ybGQh" 35 | } 36 | 37 | "encode Json as Base64" in { 38 | Base64.encode(Json.obj("word" -> "Hello World!")) must be equalTo "eyJ3b3JkIjoiSGVsbG8gV29ybGQhIn0=" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/services/AvatarService.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: SecureSocial (https://github.com/jaliss/securesocial) 3 | * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss 4 | * 5 | * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) 6 | * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package play.silhouette.api.services 21 | 22 | import scala.concurrent.Future 23 | 24 | /** 25 | * Service to retrieve avatar URLs from an avatar service such as Gravatar. 26 | */ 27 | trait AvatarService { 28 | 29 | /** 30 | * Retrieves the URL for an identifier. 31 | * 32 | * @param id The identifier for the avatar. 33 | * @return Maybe an avatar URL or None if no URL could be found for the given identifier. 34 | */ 35 | def retrieveURL(id: String): Future[Option[String]] 36 | } 37 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/google.success.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceName": "people/109476598527568979481", 3 | "etag": "%EggBAj0DCT43LohDUMMYVALUEgbwgJCgsMIgxkaGtKb3cwenMxMD1=", 4 | "emailAddresses": [ 5 | { 6 | "metadata": { 7 | "primary": true, 8 | "verified": true, 9 | "source": { 10 | "type": "ACCOUNT", 11 | "id": "109476598527568979481" 12 | } 13 | }, 14 | "value": "apollonia.vanova@watchmen.com" 15 | }, 16 | { 17 | "metadata": { 18 | "verified": true, 19 | "source": { 20 | "type": "ACCOUNT", 21 | "id": "109476598527568979481" 22 | } 23 | }, 24 | "value": "home@watchmen.com" 25 | } 26 | ], 27 | "names": [ 28 | { 29 | "metadata": { 30 | "primary": true, 31 | "source": { 32 | "type": "PROFILE", 33 | "id": "109476598527568979481" 34 | } 35 | }, 36 | "displayName": "Apollonia Vanova", 37 | "familyName": "Vanova", 38 | "givenName": "Apollonia", 39 | "displayNameLastFirst": "Vanova, Apollonia" 40 | } 41 | ], 42 | "photos": [ 43 | { 44 | "metadata": { 45 | "primary": true, 46 | "source": { 47 | "type": "PROFILE", 48 | "id": "109476598527568979481" 49 | } 50 | }, 51 | "url": "https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50" 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /silhouette-persistence/src/test/scala/play/silhouette/test/WaitPatience.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.test 17 | 18 | import org.specs2.concurrent.ExecutionEnv 19 | import org.specs2.matcher.FutureMatchers.FutureMatchable 20 | import org.specs2.matcher.Matcher 21 | 22 | import scala.concurrent.Future 23 | import scala.concurrent.duration._ 24 | 25 | /** 26 | * Helper to wait with patience to a result. 27 | * 28 | * This is needed to prevent the tests for timeouts. 29 | */ 30 | trait WaitPatience { 31 | 32 | def retries = 10 33 | 34 | def timeout: FiniteDuration = 1.second 35 | 36 | implicit class WaitWithPatienceFutureMatchable[T](m: Matcher[T])(implicit ee: ExecutionEnv) extends FutureMatchable[T](m)(ee) { 37 | def awaitWithPatience: Matcher[Future[T]] = { 38 | await(retries, timeout) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/util/IDGenerator.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: SecureSocial (https://github.com/jaliss/securesocial) 3 | * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss 4 | * 5 | * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) 6 | * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package play.silhouette.api.util 21 | 22 | import scala.concurrent.Future 23 | 24 | /** 25 | * A generator which creates an ID. 26 | */ 27 | trait IDGenerator { 28 | 29 | /** 30 | * Generates an ID. 31 | * 32 | * Generating secure IDs can block the application, while the system waits for resources. Therefore we 33 | * return a future so that the application doesn't get blocked while waiting for the generated ID. 34 | * 35 | * @return The generated ID. 36 | */ 37 | def generate: Future[String] 38 | } 39 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/google.img.non-default.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceName": "people/109476598527568979481", 3 | "etag": "%EggBAj0DCT43LohDUMMYVALUEgbwgJCgsMIgxkaGtKb3cwenMxMD1=", 4 | "emailAddresses": [ 5 | { 6 | "metadata": { 7 | "primary": true, 8 | "verified": true, 9 | "source": { 10 | "type": "ACCOUNT", 11 | "id": "109476598527568979481" 12 | } 13 | }, 14 | "value": "apollonia.vanova@watchmen.com" 15 | }, 16 | { 17 | "metadata": { 18 | "primary": true, 19 | "verified": true, 20 | "source": { 21 | "type": "HOME", 22 | "id": "109476598527568979481" 23 | } 24 | }, 25 | "value": "home@watchmen.com" 26 | } 27 | ], 28 | "names": [ 29 | { 30 | "metadata": { 31 | "primary": true, 32 | "source": { 33 | "type": "PROFILE", 34 | "id": "109476598527568979481" 35 | } 36 | }, 37 | "displayName": "Apollonia Vanova", 38 | "familyName": "Vanova", 39 | "givenName": "Apollonia", 40 | "displayNameLastFirst": "Vanova, Apollonia" 41 | } 42 | ], 43 | "photos": [ 44 | { 45 | "metadata": { 46 | "primary": true, 47 | "source": { 48 | "type": "PROFILE", 49 | "id": "109476598527568979481" 50 | } 51 | }, 52 | "url": "https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50" 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/crypto/Crypter.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.crypto 17 | 18 | /** 19 | * Crypter interface. 20 | * 21 | * This trait provides a generic encryption/decryption interface for the core, for which a concrete 22 | * implementation can be provided in userland. 23 | * 24 | * It's not guaranteed that the concrete implementations are compatible to each other. This means that 25 | * they cannot act as drop-in replacements. 26 | */ 27 | trait Crypter { 28 | 29 | /** 30 | * Encrypts a string. 31 | * 32 | * @param value The plain text to encrypt. 33 | * @return The encrypted string. 34 | */ 35 | def encrypt(value: String): String 36 | 37 | /** 38 | * Decrypts a string. 39 | * 40 | * @param value The value to decrypt. 41 | * @return The plain text string. 42 | */ 43 | def decrypt(value: String): String 44 | } 45 | -------------------------------------------------------------------------------- /silhouette/test/resources/providers/oauth2/google.img.default.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceName": "people/109476598527568979481", 3 | "etag": "%EggBAj0DCT43LohDUMMYVALUEgbwgJCgsMIgxkaGtKb3cwenMxMD1=", 4 | "emailAddresses": [ 5 | { 6 | "metadata": { 7 | "primary": true, 8 | "verified": true, 9 | "source": { 10 | "type": "ACCOUNT", 11 | "id": "109476598527568979481" 12 | } 13 | }, 14 | "value": "apollonia.vanova@watchmen.com" 15 | }, 16 | { 17 | "metadata": { 18 | "primary": true, 19 | "verified": true, 20 | "source": { 21 | "type": "HOME", 22 | "id": "109476598527568979481" 23 | } 24 | }, 25 | "value": "home@watchmen.com" 26 | } 27 | ], 28 | "names": [ 29 | { 30 | "metadata": { 31 | "primary": true, 32 | "source": { 33 | "type": "PROFILE", 34 | "id": "109476598527568979481" 35 | } 36 | }, 37 | "displayName": "Apollonia Vanova", 38 | "familyName": "Vanova", 39 | "givenName": "Apollonia", 40 | "displayNameLastFirst": "Vanova, Apollonia" 41 | } 42 | ], 43 | "photos": [ 44 | { 45 | "metadata": { 46 | "primary": true, 47 | "source": { 48 | "type": "PROFILE", 49 | "id": "109476598527568979481" 50 | } 51 | }, 52 | "url": "https://lh6.googleusercontent.com/-m34A6I77dJU/ASASAASADAAI/AVABAAAAAJk/5cg1hcjo_4s/photo.jpg?sz=50", 53 | "default": true 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/api/util/PlayHTTPLayerSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.util 17 | 18 | import play.api.libs.ws.{ WSClient, WSRequest } 19 | import play.api.test._ 20 | 21 | import scala.concurrent.ExecutionContext.Implicits.global 22 | 23 | /** 24 | * Test case for the [[play.silhouette.api.util.PlayHTTPLayer]] class. 25 | */ 26 | class PlayHTTPLayerSpec extends PlaySpecification { 27 | 28 | "The `url` method" should { 29 | "return a new WS.WSRequest instance" in new WithApplication { 30 | override def running() = { 31 | val url = "http://silhouette.mohiva.com" 32 | val client = app.injector.instanceOf[WSClient] 33 | val httpLayer = new PlayHTTPLayer(client) 34 | val requestHolder = httpLayer.url(url) 35 | 36 | requestHolder should beAnInstanceOf[WSRequest] 37 | requestHolder.url must be equalTo url 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/impl/providers/openid/service/PlayOpenIDServiceSpec.scala: -------------------------------------------------------------------------------- 1 | package play.silhouette.impl.providers.openid.service 2 | 3 | import play.silhouette.impl.providers.OpenIDSettings 4 | import play.silhouette.impl.providers.openid.services.PlayOpenIDService 5 | import org.specs2.specification.Scope 6 | import org.mockito.Mockito.mock 7 | import play.api.libs.openid.OpenIdClient 8 | import play.api.test.{ PlaySpecification, WithApplication } 9 | 10 | class PlayOpenIDServiceSpec extends PlaySpecification { 11 | 12 | "The `withSettings` method" should { 13 | "create a new instance with customized settings" in new WithApplication with Context { 14 | override def running() = { 15 | val s = service.withSettings { s => 16 | s.copy("new-provider-url") 17 | } 18 | 19 | s.settings.providerURL must be equalTo "new-provider-url" 20 | } 21 | } 22 | } 23 | 24 | /** 25 | * The context. 26 | */ 27 | trait Context extends Scope { 28 | /** 29 | * The OpenID settings. 30 | */ 31 | lazy val openIDSettings = OpenIDSettings( 32 | providerURL = "https://me.yahoo.com/", 33 | callbackURL = "http://localhost:9000/authenticate/yahoo", 34 | axRequired = Map( 35 | "fullname" -> "http://axschema.org/namePerson", 36 | "email" -> "http://axschema.org/contact/email", 37 | "image" -> "http://axschema.org/media/image/default"), 38 | realm = Some("http://localhost:9000")) 39 | 40 | lazy val service = new PlayOpenIDService(mock(classOf[OpenIdClient]), openIDSettings) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /silhouette-persistence/src/main/scala/play/silhouette/persistence/daos/DelegableAuthInfoDAO.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.persistence.daos 17 | 18 | import play.silhouette.api.AuthInfo 19 | 20 | import scala.reflect.ClassTag 21 | 22 | /** 23 | * An implementation of the auth info DAO. 24 | * 25 | * This abstract implementation of the [[play.silhouette.persistence.daos.AuthInfoDAO]] trait 26 | * allows us to get the class tag of the auth info it is responsible for. Based on the class tag 27 | * the [[play.silhouette.persistence.repositories.DelegableAuthInfoRepository]] class 28 | * can delegate operations to the DAO which is responsible for the currently handled auth info. 29 | * 30 | * @tparam T The type of the auth info to store. 31 | */ 32 | trait DelegableAuthInfoDAO[T <: AuthInfo] extends AuthInfoDAO[T] { 33 | 34 | /** 35 | * The class tag for the type parameter. 36 | */ 37 | val classTag: ClassTag[T] 38 | } 39 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/LoginInfo.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api 17 | 18 | import play.api.libs.json.Json 19 | import play.api.libs.json.OFormat 20 | 21 | /** 22 | * Represents a linked login for an identity (i.e. a local username/password or a Facebook/Google account). 23 | * 24 | * The login info contains the data about the provider that authenticated that identity. 25 | * 26 | * @param providerID The ID of the provider. 27 | * @param providerKey A unique key which identifies a user on this provider (userID, email, ...). 28 | */ 29 | final case class LoginInfo(providerID: String, providerKey: String) 30 | 31 | /** 32 | * The companion object of the login info. 33 | */ 34 | object LoginInfo extends ((String, String) => LoginInfo) { 35 | 36 | /** 37 | * Converts the [[play.silhouette.api.LoginInfo]] to Json and vice versa. 38 | */ 39 | implicit val jsonFormat: OFormat[LoginInfo] = Json.format[LoginInfo] 40 | } 41 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/services/IdentityService.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: SecureSocial (https://github.com/jaliss/securesocial) 3 | * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss 4 | * 5 | * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) 6 | * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package play.silhouette.api.services 21 | 22 | import play.silhouette.api.{ Identity, LoginInfo } 23 | 24 | import scala.concurrent.Future 25 | 26 | /** 27 | * A trait that provides the means to retrieve identities for the Silhouette module. 28 | */ 29 | trait IdentityService[T <: Identity] { 30 | 31 | /** 32 | * Retrieves an identity that matches the specified login info. 33 | * 34 | * @param loginInfo The login info to retrieve an identity. 35 | * @return The retrieved identity or None if no identity could be retrieved for the given login info. 36 | */ 37 | def retrieve(loginInfo: LoginInfo): Future[Option[T]] 38 | } 39 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/impl/util/SecureRandomIDGeneratorSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.util 17 | 18 | import play.api.test.PlaySpecification 19 | 20 | import scala.concurrent.ExecutionContext.Implicits.global 21 | 22 | /** 23 | * Test case for the [[play.silhouette.impl.util.SecureRandomIDGenerator]] class. 24 | */ 25 | class SecureRandomIDGeneratorSpec extends PlaySpecification { 26 | 27 | "The generator" should { 28 | "return a 128 byte length secure random number" in { 29 | val generator = new SecureRandomIDGenerator() 30 | val id = await(generator.generate) 31 | 32 | id must have size (128 * 2) 33 | id must beMatching("[a-f0-9]+") 34 | } 35 | 36 | "return a 265 byte length secure random number" in { 37 | val generator = new SecureRandomIDGenerator(256) 38 | val id = await(generator.generate) 39 | 40 | id must have size (256 * 2) 41 | id must beMatching("[a-f0-9]+") 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/util/Clock.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.util 17 | 18 | import java.time.{ Instant, ZonedDateTime, ZoneId, Clock => JavaClock } 19 | 20 | /** 21 | * A trait which provides a mockable implementation for a Clock instance. 22 | */ 23 | trait Clock extends JavaClock { 24 | 25 | /** 26 | * Gets the current DateTime. 27 | * 28 | * @return the current DateTime. 29 | */ 30 | def now: ZonedDateTime 31 | } 32 | 33 | /** 34 | * Creates a clock implementation. 35 | */ 36 | object Clock { 37 | 38 | /** 39 | * Gets a Clock implementation. 40 | * 41 | * @return A Clock implementation. 42 | */ 43 | def apply(): Clock = Clock(JavaClock.systemDefaultZone) 44 | 45 | def apply(clock: JavaClock): Clock = new Clock { 46 | def now: ZonedDateTime = ZonedDateTime.now(clock) 47 | 48 | def getZone: ZoneId = clock.getZone 49 | 50 | override def withZone(zone: ZoneId): Clock = Clock(clock.withZone(zone)) 51 | 52 | def instant(): Instant = clock.instant 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/User.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: SecureSocial (https://github.com/jaliss/securesocial) 3 | * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss 4 | * 5 | * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) 6 | * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package play.silhouette.impl 21 | 22 | import play.silhouette.api.{ Identity, LoginInfo } 23 | 24 | /** 25 | * The default implementation of Identity. 26 | * 27 | * @param loginInfo The linked login info. 28 | * @param firstName Maybe the first name of the authenticated user. 29 | * @param lastName Maybe the last name of the authenticated user. 30 | * @param fullName Maybe the full name of the authenticated user. 31 | * @param email Maybe the email of the authenticated provider. 32 | * @param avatarURL Maybe the avatar URL of the authenticated provider. 33 | */ 34 | final case class User( 35 | loginInfo: LoginInfo, 36 | firstName: Option[String], 37 | lastName: Option[String], 38 | fullName: Option[String], 39 | email: Option[String], 40 | avatarURL: Option[String]) extends Identity 41 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/crypto/Base64.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.crypto 17 | 18 | import play.api.libs.json.JsValue 19 | 20 | /** 21 | * Base64 helper. 22 | */ 23 | object Base64 { 24 | 25 | /** 26 | * Decodes a Base64 string. 27 | * 28 | * @param str The string to decode. 29 | * @return The decoded string. 30 | */ 31 | def decode(str: String): String = new String(java.util.Base64.getDecoder.decode(str), "UTF-8") 32 | 33 | /** 34 | * Encodes a byte array as Base64. 35 | * 36 | * @param bytes The byte array to encode. 37 | * @return The encodes string. 38 | */ 39 | def encode(bytes: Array[Byte]): String = java.util.Base64.getEncoder.encodeToString(bytes) 40 | 41 | /** 42 | * Encodes a string as Base64. 43 | * 44 | * @param str The string to encode. 45 | * @return The encodes string. 46 | */ 47 | def encode(str: String): String = encode(str.getBytes("UTF-8")) 48 | 49 | /** 50 | * Encodes a Json value as Base64. 51 | * 52 | * @param json The json value to encode. 53 | * @return The encoded value. 54 | */ 55 | def encode(json: JsValue): String = encode(json.toString()) 56 | } 57 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/api/util/ValueParserSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) from 2022 The Play Framework Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.util 17 | 18 | import org.specs2.mutable.Specification 19 | 20 | class ValueParserSpec extends Specification { 21 | 22 | "DefaultValueParser" should { 23 | "return the raw value wrapped in Some" in { 24 | DefaultValueParser.parseValue("abc123") must beSome("abc123") 25 | } 26 | } 27 | 28 | "BearerValueParser" should { 29 | "return the token without Bearer prefix" in { 30 | BearerValueParser.parseValue("Bearer abc123") must beSome("abc123") 31 | } 32 | 33 | "trim whitespace after Bearer prefix" in { 34 | BearerValueParser.parseValue("Bearer xyz789") must beSome("xyz789") 35 | } 36 | 37 | "return None if prefix is missing" in { 38 | BearerValueParser.parseValue("xyz789") must beNone 39 | } 40 | 41 | "return None if input is just 'Bearer'" in { 42 | BearerValueParser.parseValue("Bearer") must beNone 43 | } 44 | 45 | "return None if prefix is case-sensitive mismatch" in { 46 | BearerValueParser.parseValue("bearer abc123") must beNone 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/util/ValueParser.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) from 2022 The Play Framework Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.util 17 | 18 | trait ValueParser { 19 | 20 | /** 21 | * Parses a value from a field, be it headers, JSON body, etc. 22 | * 23 | * @param raw The raw value to parse 24 | * @return The parsed value 25 | */ 26 | def parseValue(raw: String): Option[String] 27 | } 28 | 29 | object DefaultValueParser extends ValueParser { 30 | 31 | /** 32 | * Parses a value from a field, be it headers, JSON body, etc. This is the default [[ValueParser]], and just returns the string as an option. 33 | * 34 | * @param raw The raw value to parse 35 | * @return The parsed value 36 | */ 37 | def parseValue(raw: String): Option[String] = Some(raw) 38 | 39 | } 40 | 41 | object BearerValueParser extends ValueParser { 42 | 43 | /** 44 | * Parses a value from a field, be it headers, JSON body, etc. 45 | * 46 | * @param raw The raw value to parse 47 | * @return The parsed value 48 | */ 49 | def parseValue(raw: String): Option[String] = { 50 | if (raw.startsWith("Bearer ")) Some(raw.stripPrefix("Bearer ").trim) else None 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/util/CacheLayer.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.util 17 | 18 | import scala.concurrent.Future 19 | import scala.concurrent.duration.Duration 20 | import scala.reflect.ClassTag 21 | 22 | /** 23 | * A trait which provides a cache API. 24 | */ 25 | trait CacheLayer { 26 | 27 | /** 28 | * Finds a value in the cache. 29 | * 30 | * @param key The key of the item to found. 31 | * @tparam T The type of the object to return. 32 | * @return The found value or None if no value could be found. 33 | */ 34 | def find[T: ClassTag](key: String): Future[Option[T]] 35 | 36 | /** 37 | * Save a value in cache. 38 | * 39 | * @param key The item key under which the value should be saved. 40 | * @param value The value to save. 41 | * @param expiration Expiration time in seconds (0 second means eternity). 42 | * @return The value saved in cache. 43 | */ 44 | def save[T](key: String, value: T, expiration: Duration = Duration.Inf): Future[T] 45 | 46 | /** 47 | * Remove a value from the cache. 48 | * 49 | * @param key Item key. 50 | * @return An empty future to wait for removal. 51 | */ 52 | def remove(key: String): Future[Unit] 53 | } 54 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/api/util/JsonFormatsSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.util 17 | 18 | import play.silhouette.api.util.JsonFormats._ 19 | import org.specs2.matcher.JsonMatchers 20 | import play.api.libs.json.Json 21 | import play.api.test._ 22 | 23 | import scala.concurrent.duration._ 24 | 25 | /** 26 | * Test case for the [[play.silhouette.api.util.JsonFormats]] class. 27 | */ 28 | class JsonFormatsSpec extends PlaySpecification with JsonMatchers { 29 | 30 | "A implicit `FiniteDurationFormat` object" should { 31 | "convert a FiniteDuration in seconds to Json" in { 32 | val json = Json.obj("value" -> 10.seconds) 33 | 34 | json.toString() must /("value" -> 10) 35 | } 36 | 37 | "convert a FiniteDuration in minutes to Json" in { 38 | val json = Json.obj("value" -> 10.minutes) 39 | 40 | json.toString() must /("value" -> (10 * 60)) 41 | } 42 | 43 | "convert Json into a FiniteDuration in seconds" in { 44 | val json = Json.obj("value" -> 10.seconds) 45 | 46 | (json \ "value").as[FiniteDuration] must be equalTo 10.seconds 47 | } 48 | 49 | "convert Json into a FiniteDuration in minutes" in { 50 | val json = Json.obj("value" -> 10.minutes) 51 | 52 | (json \ "value").as[FiniteDuration] must be equalTo 10.minutes 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/crypto/AuthenticatorEncoder.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.crypto 17 | 18 | import jakarta.inject.Inject 19 | 20 | /** 21 | * Specifies encoding/decoding of authenticator data. 22 | */ 23 | trait AuthenticatorEncoder { 24 | 25 | /** 26 | * Encodes a string. 27 | * 28 | * @param data The data to encode. 29 | * @return The encoded data. 30 | */ 31 | def encode(data: String): String 32 | 33 | /** 34 | * Decodes a string. 35 | * 36 | * @param data The data to decode. 37 | * @return The decoded data. 38 | */ 39 | def decode(data: String): String 40 | } 41 | 42 | /** 43 | * Authenticator encoder implementation based on Base64. 44 | */ 45 | class Base64AuthenticatorEncoder extends AuthenticatorEncoder { 46 | override def encode(data: String): String = Base64.encode(data) 47 | override def decode(data: String): String = Base64.decode(data) 48 | } 49 | 50 | /** 51 | * Authenticator encoder implementation based on the [[Crypter]]. 52 | * 53 | * @param crypter The crypter instance to use for the encoder. 54 | */ 55 | class CrypterAuthenticatorEncoder @Inject() (crypter: Crypter) extends AuthenticatorEncoder { 56 | override def encode(data: String): String = crypter.encrypt(data) 57 | override def decode(data: String): String = crypter.decrypt(data) 58 | } 59 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Are you looking for help? 2 | 3 | This is an issue tracker, used to manage and track the development of Silhouette. It is not a support system and so it is not a place to ask questions or get help. If you're not sure if you have found a bug, the best place to start is with either the [forum] or [Gitter channel]. If you have a feature request, the [forum] is better than the issue tracker to discuss it. 4 | 5 | ### Silhouette Version (4.0.x / etc) 6 | 7 | 8 | ### Operating System (Ubuntu 15.10 / MacOS 10.10 / Windows 10) 9 | 10 | Use `uname -a` if on Linux. 11 | 12 | ### JDK (Oracle 1.8.0_72, OpenJDK 1.8.x, Azul Zing) 13 | 14 | Paste the output from `java -version` at the command line. 15 | 16 | ### Library Dependencies 17 | 18 | If this is an issue that involves integration with another system, include the exact version and OS of the other system, including any intermediate drivers or APIs i.e. if you connect to a PostgreSQL database, include both the version / OS of PostgreSQL and the JDBC driver version used to connect to the database. 19 | 20 | ### Expected Behavior 21 | 22 | Please describe the expected behavior of the issue, starting from the first action. 23 | 24 | 1. 25 | 2. 26 | 3. 27 | 28 | ### Actual Behavior 29 | 30 | Please provide a description of what actually happens, working from the same starting point. 31 | 32 | Be descriptive: "it doesn't work" does not describe what the behavior actually is -- instead, provide a meaningful description of the issue. Copy and paste logs, and include any URLs. Turn on internal Silhouette logging with `` if there is no log output. 33 | 34 | 1. 35 | 2. 36 | 3. 37 | 38 | ### Reproducible Test Case 39 | 40 | Please provide a PR with a failing test. 41 | 42 | If the issue is more complex or requires configuration, please provide a link to a project on GitHub that reproduces the issue. 43 | 44 | [forum]: http://discourse.silhouette.rocks/ 45 | [Gitter channel]: https://gitter.im/mohiva/play-silhouette 46 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/crypto/Hash.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.crypto 17 | 18 | import java.security.MessageDigest 19 | 20 | /** 21 | * Hash helper. 22 | */ 23 | object Hash { 24 | 25 | /** 26 | * Creates a SHA1 hash from the given string. 27 | * 28 | * @param str The string to create a hash from. 29 | * @return The SHA1 hash of the string. 30 | */ 31 | def sha1(str: String): String = sha1(str.getBytes("UTF-8")) 32 | 33 | /** 34 | * Creates a SHA1 hash from the given byte array. 35 | * 36 | * @param bytes The bytes to create a hash from. 37 | * @return The SHA1 hash of the bytes. 38 | */ 39 | def sha1(bytes: Array[Byte]): String = { 40 | MessageDigest.getInstance("SHA-1").digest(bytes).map("%02x".format(_)).mkString 41 | } 42 | 43 | /** 44 | * Creates a SHA2 hash from the given string. 45 | * 46 | * @param str The string to create a hash from. 47 | * @return The SHA2 hash of the string. 48 | */ 49 | def sha2(str: String): String = sha2(str.getBytes("UTF-8")) 50 | 51 | /** 52 | * Creates a SHA2 hash from the given byte array. 53 | * 54 | * @param bytes The bytes to create a hash from. 55 | * @return The SHA2 hash of the bytes. 56 | */ 57 | def sha2(bytes: Array[Byte]): String = { 58 | MessageDigest.getInstance("SHA-256").digest(bytes).map("%02x".format(_)).mkString 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/repositories/AuthenticatorRepository.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.repositories 17 | 18 | import play.silhouette.api.StorableAuthenticator 19 | 20 | import scala.concurrent.Future 21 | 22 | /** 23 | * A trait that provides the means to persist authenticator information for the Silhouette module. 24 | * 25 | * @tparam T The type of the authenticator to store. 26 | */ 27 | trait AuthenticatorRepository[T <: StorableAuthenticator] { 28 | 29 | /** 30 | * Finds the authenticator for the given ID. 31 | * 32 | * @param id The authenticator ID. 33 | * @return The found authenticator or None if no authenticator could be found for the given ID. 34 | */ 35 | def find(id: String): Future[Option[T]] 36 | 37 | /** 38 | * Adds a new authenticator. 39 | * 40 | * @param authenticator The authenticator to add. 41 | * @return The added authenticator. 42 | */ 43 | def add(authenticator: T): Future[T] 44 | 45 | /** 46 | * Updates an already existing authenticator. 47 | * 48 | * @param authenticator The authenticator to update. 49 | * @return The updated authenticator. 50 | */ 51 | def update(authenticator: T): Future[T] 52 | 53 | /** 54 | * Removes the authenticator for the given ID. 55 | * 56 | * @param id The authenticator ID. 57 | * @return An empty future. 58 | */ 59 | def remove(id: String): Future[Unit] 60 | } 61 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/Provider.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api 17 | 18 | import play.api.mvc.Request 19 | 20 | import scala.concurrent.Future 21 | 22 | /** 23 | * A marker interface for all providers. 24 | */ 25 | trait Provider { 26 | 27 | /** 28 | * Gets the provider ID. 29 | * 30 | * @return The provider ID. 31 | */ 32 | def id: String 33 | } 34 | 35 | /** 36 | * A provider which can be hooked into a request. 37 | * 38 | * It scans the request for credentials and returns the login info for it. 39 | */ 40 | trait RequestProvider extends Provider { 41 | 42 | /** 43 | * Authenticates an identity based on credentials sent in a request. 44 | * 45 | * Silhouette supports chaining of request providers. So if more as one request provider is defined 46 | * it tries to authenticate until one provider returns an identity. To control the behaviour of the 47 | * chaining you can use the return type by this method. 48 | * 49 | * None - If returning None, then the next provider in the chain will be executed. 50 | * Some(identity) - If returning some identity, then this provider will be used for authentication. 51 | * Exception - Throwing an exception breaks the chain. The error handler will also handle this exception. 52 | * 53 | * @param request The request. 54 | * @tparam B The type of the body. 55 | * @return Some login info on successful authentication or None if the authentication was unsuccessful. 56 | */ 57 | def authenticate[B](request: Request[B]): Future[Option[LoginInfo]] 58 | } 59 | -------------------------------------------------------------------------------- /silhouette-crypto-jca/src/test/scala/play/silhouette/crypto/JcaCrypterSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.crypto 17 | 18 | import play.silhouette.api.exceptions.CryptoException 19 | import play.silhouette.crypto.JcaCrypter._ 20 | import org.specs2.mutable.Specification 21 | import org.specs2.specification.Scope 22 | 23 | /** 24 | * Test case for the [[JcaCrypter]] class. 25 | */ 26 | class JcaCrypterSpec extends Specification { 27 | 28 | "The `decrypt` method" should { 29 | "throw a CryptoException if the format is unexpected" in new Context { 30 | encoder.decrypt("data") must throwA[CryptoException].like { 31 | case e => 32 | e.getMessage must be equalTo UnexpectedFormat 33 | } 34 | } 35 | 36 | "throw a CryptoException if the version is unknown" in new Context { 37 | encoder.decrypt("2-data") must throwA[CryptoException].like { 38 | case e => 39 | e.getMessage must be equalTo UnknownVersion.format(2) 40 | } 41 | } 42 | } 43 | 44 | "The crypter" should { 45 | "encrypt/decrypt a string" in new Context { 46 | val text = "Silhouette rocks" 47 | encoder.decrypt(encoder.encrypt(text)) must be equalTo text 48 | } 49 | } 50 | 51 | /** 52 | * The context. 53 | */ 54 | trait Context extends Scope { 55 | 56 | /** 57 | * The encryption key. 58 | */ 59 | val key = "s3cr3t_k3y" 60 | 61 | /** 62 | * The settings instance. 63 | */ 64 | val settings = new JcaCrypterSettings(key) 65 | 66 | /** 67 | * The crypter to test. 68 | */ 69 | val encoder = new JcaCrypter(settings) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/util/PlayCacheLayer.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.util 17 | 18 | import jakarta.inject.Inject 19 | 20 | import play.silhouette.api.util.CacheLayer 21 | import play.api.cache.AsyncCacheApi 22 | 23 | import scala.concurrent.Future 24 | import scala.concurrent.duration.Duration 25 | import scala.reflect.ClassTag 26 | 27 | /** 28 | * Implementation of the cache layer which uses the default Play cache plugin. 29 | * 30 | * @param cacheApi Plays cache API implementation. 31 | */ 32 | class PlayCacheLayer @Inject() (cacheApi: AsyncCacheApi) extends CacheLayer { 33 | 34 | /** 35 | * Save a value in cache. 36 | * 37 | * @param key The item key under which the value should be saved. 38 | * @param value The value to save. 39 | * @param expiration Expiration time in seconds (0 second means eternity). 40 | * @return The value saved in cache. 41 | */ 42 | override def save[T](key: String, value: T, expiration: Duration = Duration.Inf): Future[T] = Future.successful { 43 | cacheApi.set(key, value, expiration) 44 | value 45 | } 46 | 47 | /** 48 | * Finds a value in the cache. 49 | * 50 | * @param key The key of the item to found. 51 | * @tparam T The type of the object to return. 52 | * @return The found value or None if no value could be found. 53 | */ 54 | override def find[T: ClassTag](key: String): Future[Option[T]] = cacheApi.get[T](key) 55 | 56 | /** 57 | * Remove a value from the cache. 58 | * 59 | * @param key Item key. 60 | * @return An empty future to wait for removal. 61 | */ 62 | override def remove(key: String) = Future.successful(cacheApi.remove(key)) 63 | } 64 | -------------------------------------------------------------------------------- /project/Dependencies.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | import sbt._ 15 | 16 | object Dependencies { 17 | 18 | object Library { 19 | 20 | val updates: Seq[ModuleID] = Seq( 21 | "commons-io" % "commons-io" % "2.21.0" 22 | ) 23 | 24 | object Play { 25 | val version: String = play.core.PlayVersion.current 26 | val ws = "org.playframework" %% "play-ws" % version 27 | val cache = "org.playframework" %% "play-cache" % version 28 | val test = "org.playframework" %% "play-test" % version 29 | val specs2 = "org.playframework" %% "play-specs2" % version 30 | val openid = "org.playframework" %% "play-openid" % version 31 | } 32 | 33 | object Specs2 { 34 | private val version = "4.23.0" 35 | val core = "org.specs2" %% "specs2-core" % version 36 | val matcherExtra = "org.specs2" %% "specs2-matcher-extra" % version 37 | } 38 | 39 | val argon2 = "de.mkammerer" % "argon2-jvm" % "2.12" 40 | val commonsCodec = "commons-codec" % "commons-codec" % "1.20.0" 41 | val jbcrypt = "de.svenkubiak" % "jBCrypt" % "0.4.3" 42 | val jwt = "com.auth0" % "java-jwt" % "4.5.0" 43 | val scalaGuice = "net.codingwell" %% "scala-guice" % "7.0.0" 44 | val pekkoTestkit = "org.apache.pekko" %% "pekko-testkit" % play.core.PlayVersion.pekkoVersion 45 | val mockito = "org.mockito" % "mockito-core" % "5.21.0" 46 | val casClient = "org.apereo.cas.client" % "cas-client-core" % "4.0.4" 47 | val casClientSupportSAML = "org.apereo.cas.client" % "cas-client-support-saml" % "4.0.4" 48 | val apacheCommonLang = "org.apache.commons" % "commons-lang3" % "3.20.0" 49 | val googleAuth = "com.warrenstrange" % "googleauth" % "1.5.0" 50 | val izumiReflect = "dev.zio" %% "izumi-reflect" % "3.0.9" // Scala 3 replacement for scala 2 reflect universe 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/impl/providers/PasswordProviderSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.providers 17 | 18 | import play.silhouette.api.repositories.AuthInfoRepository 19 | import play.silhouette.api.util._ 20 | import org.specs2.specification.Scope 21 | import org.mockito.Mockito._ 22 | import org.mockito.ArgumentMatchers.any 23 | import test.Helper.mock 24 | import play.api.test.PlaySpecification 25 | 26 | /** 27 | * Abstract test case for the [[play.silhouette.impl.providers.PasswordProvider]] based class. 28 | */ 29 | trait PasswordProviderSpec extends PlaySpecification { 30 | 31 | /** 32 | * The context. 33 | */ 34 | trait BaseContext extends Scope { 35 | 36 | /** 37 | * The default password hasher. 38 | */ 39 | lazy val fooHasher = hasher("foo") 40 | 41 | /** 42 | * A deprecated password hasher. 43 | */ 44 | lazy val barHasher = hasher("bar") 45 | 46 | /** 47 | * The auth info repository mock. 48 | */ 49 | lazy val authInfoRepository = mock[AuthInfoRepository] 50 | 51 | /** 52 | * The password hasher registry. 53 | */ 54 | lazy val passwordHasherRegistry = new PasswordHasherRegistry(fooHasher, Seq(barHasher)) 55 | 56 | /** 57 | * Helper method to create a hasher mock. 58 | * 59 | * @param id The ID of the hasher. 60 | * @return A hasher mock. 61 | */ 62 | private def hasher(id: String) = { 63 | val h = mock[PasswordHasher] 64 | when(h.id).thenReturn(id) 65 | when(h.isSuitable(any())).thenAnswer { p => 66 | p.getArgument(0).asInstanceOf[PasswordInfo].hasher == h.id 67 | } 68 | when(h.isDeprecated(any())).thenReturn(Some(false)) 69 | h 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/impl/util/PlayCacheLayerSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.util 17 | 18 | import org.mockito.Mockito._ 19 | import org.specs2.specification.Scope 20 | import play.api.cache.AsyncCacheApi 21 | import play.api.test.PlaySpecification 22 | 23 | import java.time.ZonedDateTime 24 | import scala.concurrent.Future 25 | import scala.concurrent.duration.Duration 26 | 27 | /** 28 | * Test case for the [[play.silhouette.impl.util.PlayCacheLayer]] class. 29 | */ 30 | class PlayCacheLayerSpec extends PlaySpecification { 31 | 32 | "The `find` method" should { 33 | "return value from cache" in new Context { 34 | when(cacheAPI.get[ZonedDateTime]("id")).thenReturn(Future.successful(Some(value))) 35 | 36 | await(layer.find[ZonedDateTime]("id")) should beSome(value) 37 | 38 | verify(cacheAPI).get[ZonedDateTime]("id") 39 | } 40 | } 41 | 42 | "The `save` method" should { 43 | "save value in cache" in new Context { 44 | await(layer.save("id", value)) 45 | 46 | verify(cacheAPI).set("id", value, Duration.Inf) 47 | } 48 | } 49 | 50 | "The `remove` method" should { 51 | "removes value from cache" in new Context { 52 | await(layer.remove("id")) must beEqualTo(()) 53 | 54 | verify(cacheAPI).remove("id") 55 | } 56 | } 57 | 58 | /** 59 | * The context. 60 | */ 61 | trait Context extends Scope { 62 | 63 | /** 64 | * The cache API. 65 | */ 66 | lazy val cacheAPI = mock(classOf[AsyncCacheApi]) 67 | 68 | /** 69 | * The layer to test. 70 | */ 71 | lazy val layer = new PlayCacheLayer(cacheAPI) 72 | 73 | /** 74 | * The value to cache. 75 | */ 76 | lazy val value = ZonedDateTime.now 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/util/SecureRandomIDGenerator.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: SecureSocial (https://github.com/jaliss/securesocial) 3 | * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss 4 | * 5 | * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) 6 | * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package play.silhouette.impl.util 21 | 22 | import java.security.SecureRandom 23 | 24 | import play.silhouette.api.util.IDGenerator 25 | import play.api.libs.Codecs 26 | 27 | import scala.concurrent.{ ExecutionContext, Future } 28 | 29 | /** 30 | * A generator which uses SecureRandom to generate cryptographically strong IDs. 31 | * 32 | * @param idSizeInBytes The size of the ID length in bytes. 33 | * @param ec The execution context to handle the asynchronous operations. 34 | */ 35 | class SecureRandomIDGenerator(idSizeInBytes: Int = 128)(implicit ec: ExecutionContext) extends IDGenerator { 36 | 37 | /** 38 | * Generates a new ID using SecureRandom. 39 | * 40 | * @return The generated ID. 41 | */ 42 | override def generate: Future[String] = { 43 | val randomValue = new Array[Byte](idSizeInBytes) 44 | Future(SecureRandomIDGenerator.random.nextBytes(randomValue)).map { _ => 45 | Codecs.toHexString(randomValue) 46 | } 47 | } 48 | } 49 | 50 | /** 51 | * The companion object. 52 | */ 53 | object SecureRandomIDGenerator { 54 | 55 | /** 56 | * A cryptographically strong random number generator (RNG). 57 | * 58 | * There is a cost of getting a secure random instance for its initial seeding, so it's recommended you use 59 | * a singleton style so you only create one for all of your usage going forward. 60 | * 61 | * On Linux systems SecureRandom uses /dev/random and it can block waiting for sufficient entropy to build up. 62 | */ 63 | lazy val random = new SecureRandom() 64 | } 65 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/api/AuthenticatorSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api 17 | 18 | import play.silhouette.api.Authenticator.Implicits._ 19 | import play.api.test.PlaySpecification 20 | 21 | import java.time.{ ZoneId, ZonedDateTime } 22 | import scala.concurrent.duration._ 23 | 24 | /** 25 | * Test case for the [[play.silhouette.api.Authenticator]] class. 26 | */ 27 | class AuthenticatorSpec extends PlaySpecification { 28 | 29 | "The + method of the RichDateTime class" should { 30 | "add a second to a DateTime instance" in { 31 | ZonedDateTime.of(2015, 6, 16, 19, 46, 0, 0, ZoneId.systemDefault) + 1.second must be equalTo ZonedDateTime.of(2015, 6, 16, 19, 46, 1, 0, ZoneId.systemDefault) 32 | } 33 | 34 | "add a minute to a DateTime instance" in { 35 | ZonedDateTime.of(2015, 6, 16, 19, 46, 0, 0, ZoneId.systemDefault) + 1.minute must be equalTo ZonedDateTime.of(2015, 6, 16, 19, 47, 0, 0, ZoneId.systemDefault) 36 | } 37 | 38 | "add an hour to a DateTime instance" in { 39 | ZonedDateTime.of(2015, 6, 16, 19, 46, 0, 0, ZoneId.systemDefault) + 1.hour must be equalTo ZonedDateTime.of(2015, 6, 16, 20, 46, 0, 0, ZoneId.systemDefault) 40 | } 41 | 42 | "subtract a second from a DateTime instance" in { 43 | ZonedDateTime.of(2015, 6, 16, 19, 46, 0, 0, ZoneId.systemDefault) - 1.second must be equalTo ZonedDateTime.of(2015, 6, 16, 19, 45, 59, 0, ZoneId.systemDefault) 44 | } 45 | 46 | "subtract a minute from a DateTime instance" in { 47 | ZonedDateTime.of(2015, 6, 16, 19, 46, 0, 0, ZoneId.systemDefault) - 1.minute must be equalTo ZonedDateTime.of(2015, 6, 16, 19, 45, 0, 0, ZoneId.systemDefault) 48 | } 49 | 50 | "subtract an hour from a DateTime instance" in { 51 | ZonedDateTime.of(2015, 6, 16, 19, 46, 0, 0, ZoneId.systemDefault) - 1.hour must be equalTo ZonedDateTime.of(2015, 6, 16, 18, 46, 0, 0, ZoneId.systemDefault) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/api/services/AuthenticatorResultSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.services 17 | 18 | import play.api.mvc.{ Cookie, Results } 19 | import play.api.test.{ WithApplication, PlaySpecification } 20 | 21 | /** 22 | * Test case for the [[play.silhouette.api.services.AuthenticatorResult]] class. 23 | */ 24 | class AuthenticatorResultSpec extends PlaySpecification { 25 | 26 | "The `copy` method" should { 27 | "return new a new instance of an authenticator result" in { 28 | val result = Results.Ok 29 | val authenticatorResult = AuthenticatorResult(result) 30 | 31 | authenticatorResult.copy(result.header, result.body) must beAnInstanceOf[AuthenticatorResult] 32 | } 33 | } 34 | 35 | "The `withSession` method" should { 36 | "return new a new instance of an authenticator result" in new WithApplication { 37 | override def running() = { 38 | val result = Results.Ok 39 | val authenticatorResult = AuthenticatorResult(result) 40 | 41 | authenticatorResult.withSession("name" -> "value") must beAnInstanceOf[AuthenticatorResult] 42 | } 43 | } 44 | } 45 | 46 | "The `withCookies` method" should { 47 | "return new a new instance of an authenticator result" in new WithApplication { 48 | override def running() = { 49 | val result = Results.Ok 50 | val authenticatorResult = AuthenticatorResult(result) 51 | 52 | authenticatorResult.withCookies(Cookie("name", "value")) must beAnInstanceOf[AuthenticatorResult] 53 | } 54 | } 55 | } 56 | 57 | "The `withHeaders` method" should { 58 | "return new a new instance of an authenticator result" in new WithApplication { 59 | override def running() = { 60 | val result = Results.Ok 61 | val authenticatorResult = AuthenticatorResult(result) 62 | 63 | authenticatorResult.withHeaders("name" -> "value") must beAnInstanceOf[AuthenticatorResult] 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/util/DefaultFingerprintGenerator.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: SecureSocial (https://github.com/jaliss/securesocial) 3 | * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss 4 | * 5 | * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) 6 | * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package play.silhouette.impl.util 21 | 22 | import play.silhouette.api.crypto.Hash 23 | import play.silhouette.api.util.FingerprintGenerator 24 | import play.api.http.HeaderNames._ 25 | import play.api.mvc.RequestHeader 26 | 27 | /** 28 | * A generator which creates a SHA1 fingerprint from `User-Agent`, `Accept-Language`, `Accept-Charset` 29 | * and `Accept-Encoding` headers and if defined the remote address of the user. 30 | * 31 | * The `Accept` header would also be a good candidate, but this header makes problems in applications 32 | * which uses content negotiation. So the default fingerprint generator doesn't include it. 33 | * 34 | * The same with `Accept-Encoding`. But in Chromium/Blink based browser the content of this header may 35 | * be changed during requests. @see https://github.com/mohiva/play-silhouette/issues/277 36 | * 37 | * @param includeRemoteAddress Indicates if the remote address should be included into the fingerprint. 38 | */ 39 | class DefaultFingerprintGenerator(includeRemoteAddress: Boolean = false) extends FingerprintGenerator { 40 | 41 | /** 42 | * Generates a fingerprint from request. 43 | * 44 | * @param request The request header. 45 | * @return The generated fingerprint. 46 | */ 47 | override def generate(implicit request: RequestHeader) = { 48 | Hash.sha1(new StringBuilder() 49 | .append(request.headers.get(USER_AGENT).getOrElse("")).append(":") 50 | .append(request.headers.get(ACCEPT_LANGUAGE).getOrElse("")).append(":") 51 | .append(request.headers.get(ACCEPT_CHARSET).getOrElse("")).append(":") 52 | .append(if (includeRemoteAddress) request.remoteAddress else "") 53 | .toString()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /silhouette-persistence/src/main/scala/play/silhouette/persistence/repositories/CacheAuthenticatorRepository.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.persistence.repositories 17 | 18 | import jakarta.inject.Inject 19 | 20 | import play.silhouette.api.StorableAuthenticator 21 | import play.silhouette.api.repositories.AuthenticatorRepository 22 | import play.silhouette.api.util.CacheLayer 23 | 24 | import scala.concurrent.Future 25 | import scala.concurrent.duration.Duration 26 | import scala.reflect.ClassTag 27 | 28 | /** 29 | * Implementation of the authenticator repository which uses the cache layer to persist the authenticator. 30 | * 31 | * @param cacheLayer The cache layer implementation. 32 | * @tparam T The type of the authenticator to store. 33 | */ 34 | class CacheAuthenticatorRepository[T <: StorableAuthenticator: ClassTag] @Inject() (cacheLayer: CacheLayer) 35 | extends AuthenticatorRepository[T] { 36 | 37 | /** 38 | * Finds the authenticator for the given ID. 39 | * 40 | * @param id The authenticator ID. 41 | * @return The found authenticator or None if no authenticator could be found for the given ID. 42 | */ 43 | override def find(id: String): Future[Option[T]] = cacheLayer.find[T](id) 44 | 45 | /** 46 | * Adds a new authenticator. 47 | * 48 | * @param authenticator The authenticator to add. 49 | * @return The added authenticator. 50 | */ 51 | override def add(authenticator: T): Future[T] = cacheLayer.save[T](authenticator.id, authenticator, Duration.Inf) 52 | 53 | /** 54 | * Updates an already existing authenticator. 55 | * 56 | * @param authenticator The authenticator to update. 57 | * @return The updated authenticator. 58 | */ 59 | override def update(authenticator: T): Future[T] = cacheLayer.save[T](authenticator.id, authenticator, Duration.Inf) 60 | 61 | /** 62 | * Removes the authenticator for the given ID. 63 | * 64 | * @param id The authenticator ID. 65 | * @return An empty future. 66 | */ 67 | override def remove(id: String): Future[Unit] = cacheLayer.remove(id) 68 | } 69 | -------------------------------------------------------------------------------- /silhouette-persistence/src/main/scala/play/silhouette/persistence/daos/AuthInfoDAO.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.persistence.daos 17 | 18 | import play.silhouette.api.{ AuthInfo, LoginInfo } 19 | 20 | import scala.concurrent.Future 21 | 22 | /** 23 | * The DAO to persist the auth info. 24 | * 25 | * @tparam T The type of the auth info to store. 26 | */ 27 | trait AuthInfoDAO[T <: AuthInfo] { 28 | 29 | /** 30 | * Finds the auth info which is linked to the specified login info. 31 | * 32 | * @param loginInfo The linked login info. 33 | * @return The found auth info or None if no auth info could be found for the given login info. 34 | */ 35 | def find(loginInfo: LoginInfo): Future[Option[T]] 36 | 37 | /** 38 | * Adds new auth info for the given login info. 39 | * 40 | * @param loginInfo The login info for which the auth info should be added. 41 | * @param authInfo The auth info to add. 42 | * @return The added auth info. 43 | */ 44 | def add(loginInfo: LoginInfo, authInfo: T): Future[T] 45 | 46 | /** 47 | * Updates the auth info for the given login info. 48 | * 49 | * @param loginInfo The login info for which the auth info should be updated. 50 | * @param authInfo The auth info to update. 51 | * @return The updated auth info. 52 | */ 53 | def update(loginInfo: LoginInfo, authInfo: T): Future[T] 54 | 55 | /** 56 | * Saves the auth info for the given login info. 57 | * 58 | * This method either adds the auth info if it doesn't exists or it updates the auth info 59 | * if it already exists. 60 | * 61 | * @param loginInfo The login info for which the auth info should be saved. 62 | * @param authInfo The auth info to save. 63 | * @return The saved auth info. 64 | */ 65 | def save(loginInfo: LoginInfo, authInfo: T): Future[T] 66 | 67 | /** 68 | * Removes the auth info for the given login info. 69 | * 70 | * @param loginInfo The login info for which the auth info should be removed. 71 | * @return A future to wait for the process to be completed. 72 | */ 73 | def remove(loginInfo: LoginInfo): Future[Unit] 74 | } 75 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/util/HTTPLayer.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.util 17 | 18 | import play.api.libs.ws._ 19 | 20 | import scala.concurrent.ExecutionContext 21 | 22 | /** 23 | * A trait which provides a mockable implementation for the HTTP layer. 24 | */ 25 | trait HTTPLayer extends ExecutionContextProvider { 26 | 27 | /** 28 | * The type of the request. 29 | */ 30 | type Request <: WSRequest 31 | 32 | /** 33 | * Prepare a new request. You can then construct it by chaining calls. 34 | * 35 | * @param url The URL to request. 36 | */ 37 | def url(url: String): Request 38 | } 39 | 40 | /** 41 | * Implementation of the HTTP layer which uses the Play web service implementation. 42 | * 43 | * It makes no sense to move the HTTPLayer implementation to the contrib package, because the complete 44 | * Silhouette module is bound to Play's HTTP implementation. So this layer exists only for mocking purpose. 45 | * 46 | * @param client Play's WS client implementation. 47 | * @param executionContext The execution context to handle the asynchronous operations. 48 | */ 49 | class PlayHTTPLayer(client: WSClient)(implicit val executionContext: ExecutionContext) extends HTTPLayer { 50 | 51 | /** 52 | * The type of the request. 53 | */ 54 | type Request = WSRequest 55 | 56 | /** 57 | * Prepare a new request. You can then construct it by chaining calls. 58 | * 59 | * @param url The URL to request. 60 | */ 61 | def url(url: String): Request = client.url(url) 62 | } 63 | 64 | /** 65 | * A mockable WS request. 66 | * 67 | * @see https://github.com/playframework/play-ws/issues/108 68 | */ 69 | trait MockWSRequest extends WSRequest { 70 | override type Self = WSRequest 71 | override type Response = WSResponse 72 | } 73 | 74 | /** 75 | * A mockable HTTP layer. 76 | */ 77 | trait MockHTTPLayer extends HTTPLayer { 78 | 79 | /** 80 | * The type of the request. 81 | */ 82 | type Request = MockWSRequest 83 | 84 | /** 85 | * Prepare a new request. You can then construct it by chaining calls. 86 | * 87 | * @param url The URL to request. 88 | */ 89 | def url(url: String): Request 90 | } 91 | -------------------------------------------------------------------------------- /silhouette-crypto-jca/src/test/scala/play/silhouette/crypto/JcaSignerSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.crypto 17 | 18 | import java.util.regex.Pattern 19 | 20 | import org.specs2.mutable.Specification 21 | import org.specs2.specification.Scope 22 | import JcaSigner._ 23 | import play.silhouette.api.exceptions.CryptoException 24 | 25 | /** 26 | * Test case for the [[JcaSigner]] class. 27 | */ 28 | class JcaSignerSpec extends Specification { 29 | 30 | "The `sign` method" should { 31 | "return a signed message in the form [VERSION]-[SIGNATURE]-[DATA]" in new Context { 32 | val data = "some-data" 33 | 34 | signer.sign(data) must beMatching(s"1-[^-]+-$data") 35 | } 36 | } 37 | 38 | "The `extract` method" should { 39 | "throw a `CryptoException` if the message format is invalid" in new Context { 40 | val msg = "^" + Pattern.quote(InvalidMessageFormat) + ".*" 41 | 42 | signer.extract("invalid") must beFailedTry.withThrowable[CryptoException](msg) 43 | } 44 | 45 | "throw a `CryptoException` if the version is unknown" in new Context { 46 | val msg = "^" + Pattern.quote(UnknownVersion.format(2)) + ".*" 47 | 48 | signer.extract("2-signature-data") must beFailedTry.withThrowable[CryptoException](msg) 49 | } 50 | 51 | "throw a `CryptoException` if the signature is invalid" in new Context { 52 | val msg = "^" + Pattern.quote(BadSignature) + ".*" 53 | 54 | signer.extract("1-signature-data") must beFailedTry.withThrowable[CryptoException](msg) 55 | } 56 | 57 | "extract a previously signed message" in new Context { 58 | val data = "some-data" 59 | val message = signer.sign(data) 60 | 61 | signer.extract(message) must beSuccessfulTry.withValue(data) 62 | } 63 | } 64 | 65 | /** 66 | * The context. 67 | */ 68 | trait Context extends Scope { 69 | 70 | /** 71 | * The encryption key. 72 | */ 73 | val key = "s3cr3t_k3y" 74 | 75 | /** 76 | * The settings instance. 77 | */ 78 | val settings = JcaSignerSettings(key) 79 | 80 | /** 81 | * The cookie signer to test. 82 | */ 83 | val signer = new JcaSigner(settings) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /silhouette-testkit/app-2/play/silhouette/test/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette 17 | 18 | import play.silhouette.api._ 19 | import play.api.mvc.Request 20 | import play.api.test.FakeRequest 21 | 22 | import scala.concurrent.duration._ 23 | import scala.concurrent.{ Await, Future } 24 | import scala.language.{ implicitConversions, postfixOps } 25 | 26 | /** 27 | * Test helpers to test a Silhouette application. 28 | */ 29 | package object test { 30 | 31 | /** 32 | * Resolves a future by waiting of the result. 33 | * 34 | * @param f The future to block. 35 | * @tparam T The type contained in the future. 36 | * @return The value contained in the future. 37 | */ 38 | implicit protected[test] def await[T](f: Future[T]): T = Await.result(f, 60 seconds) 39 | 40 | /** 41 | * Provides a method which add an authenticator to a fake request. 42 | * 43 | * @param f A fake request instance. 44 | * @tparam A The type of the body. 45 | */ 46 | implicit class FakeRequestWithAuthenticator[A](f: FakeRequest[A]) { 47 | implicit val request: FakeRequest[A] = f 48 | 49 | /** 50 | * Creates a fake request with an embedded authenticator. 51 | * 52 | * @param authenticator The authenticator to embed into the request. 53 | * @param env The Silhouette environment. 54 | * @tparam E The type of the environment. 55 | * @return A fake request. 56 | */ 57 | def withAuthenticator[E <: Env](authenticator: E#A)(implicit env: Environment[E]): FakeRequest[A] = { 58 | implicit val ec = env.executionContext 59 | val rh = env.authenticatorService.init(authenticator).map(v => env.authenticatorService.embed(v, f)) 60 | 61 | new FakeRequest(Request.apply(rh, f.body)) 62 | } 63 | 64 | /** 65 | * Creates a fake request with an embedded authenticator. 66 | * 67 | * @param loginInfo The login info for which the new authenticator should be created. 68 | * @param env The Silhouette environment. 69 | * @tparam E The type of the environment. 70 | * @return A fake request. 71 | */ 72 | def withAuthenticator[E <: Env](loginInfo: LoginInfo)(implicit env: Environment[E]): FakeRequest[A] = { 73 | withAuthenticator(FakeAuthenticator[E](loginInfo)) 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /silhouette-testkit/app-3/play/silhouette/test/package.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette 17 | 18 | import play.silhouette.api._ 19 | import play.api.mvc.Request 20 | import play.api.test.FakeRequest 21 | 22 | import scala.concurrent.duration._ 23 | import scala.concurrent.{ Await, Future } 24 | import scala.language.{ implicitConversions, postfixOps } 25 | 26 | /** 27 | * Test helpers to test a Silhouette application. 28 | */ 29 | package object test { 30 | 31 | /** 32 | * Resolves a future by waiting of the result. 33 | * 34 | * @param f The future to block. 35 | * @tparam T The type contained in the future. 36 | * @return The value contained in the future. 37 | */ 38 | implicit protected[test] def await[T](f: Future[T]): T = Await.result(f, 60 seconds) 39 | 40 | /** 41 | * Provides a method which add an authenticator to a fake request. 42 | * 43 | * @param f A fake request instance. 44 | * @tparam A The type of the body. 45 | */ 46 | implicit class FakeRequestWithAuthenticator[F](f: FakeRequest[F]) { 47 | implicit val request: FakeRequest[F] = f 48 | 49 | /** 50 | * Creates a fake request with an embedded authenticator. 51 | * 52 | * @param authenticator The authenticator to embed into the request. 53 | * @param env The Silhouette environment. 54 | * @tparam E The type of the environment. 55 | * @return A fake request. 56 | */ 57 | def withAuthenticator[E <: Env](authenticator: A[E])(implicit env: Environment[E]): FakeRequest[F] = { 58 | implicit val ec = env.executionContext 59 | val rh = env.authenticatorService.init(authenticator).map(v => env.authenticatorService.embed(v, f)) 60 | 61 | new FakeRequest(Request.apply(rh, f.body)) 62 | } 63 | 64 | /** 65 | * Creates a fake request with an embedded authenticator. 66 | * 67 | * @param loginInfo The login info for which the new authenticator should be created. 68 | * @param env The Silhouette environment. 69 | * @tparam E The type of the environment. 70 | * @return A fake request. 71 | */ 72 | def withAuthenticator[E <: Env](loginInfo: LoginInfo)(implicit env: Environment[E]): FakeRequest[F] = { 73 | withAuthenticator(FakeAuthenticator[E](loginInfo)) 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to Silhouette 2 | ========================== 3 | 4 | How to contribute 5 | ----------------- 6 | 7 | Silhouette is an open source project. Contributions are appreciated. 8 | 9 | Some ways in which you can contribute are: reporting errors, improving documentation, adding examples, adding support 10 | for more services, fixing bugs, suggesting new features, adding test cases, translating messages, and whatever else 11 | you can think of that may be helpful. If in doubt, just ask. 12 | 13 | 14 | Development workflow 15 | -------------------- 16 | 17 | Development is coordinated via [GitHub]. Ideas for improvements are discussed using the [chat] or historically were discussed using the (now unsupported) [mailing list]. 18 | 19 | To submit issues, please use the [GitHub issue tracker]. **Please do not use the issue tracker for questions, instead take to the official [chat] where we will be more than happy to help.** 20 | 21 | The documentation can be improved by submitting change requests via [readme.io]. 22 | 23 | For a more streamlined experience for all people involved, we encourage contributors to follow the practices described 24 | at [GitHub workflow for submitting pull requests]. 25 | 26 | Scala source code should follow the conventions documented in the [Scala Style Guide]. Additionally, acronyms should 27 | be capitalized. To have your code automatically reformatted, run this command before committing your changes: 28 | 29 | scripts/reformat 30 | 31 | After submitting your pull request, please [watch the result] of the automated Travis CI build and correct any reported 32 | errors or inconsistencies. 33 | 34 | 35 | Known issue 36 | --------------------- 37 | 38 | Please use the scripts under the `scripts` directory to launch sbt. 39 | 40 | You'll need sbt-launch 0.13.8 to start the scripts, it's available here: https://scala.jfrog.io/artifactory/ivy-releases/org.scala-sbt/sbt-launch/0.13.8/jars/sbt-launch.jar 41 | 42 | If you use SBT 1.5.x, start sbt with ./scripts/sbt -Dsbt.boot.directory=/tmp/boot1 -Dsbt.launcher.coursier=false 43 | If you get a weird issue (cannot redefine component. ID: org.scala-sbt-compiler-interface-0.13.18-bin_2...), remove the /tmp/boot1 directory. 44 | 45 | 46 | License and Copyright 47 | --------------------- 48 | 49 | By submitting work via pull requests, issues, documentation, or any other means, contributors indicate their agreement to 50 | publish their work under this project's license and also attest that they are the authors of the work and grant a 51 | copyright license to the Playframework Organisation, unless the contribution clearly states a different copyright notice 52 | (e.g., it contains original work by a third party). 53 | 54 | 55 | [GitHub]: https://github.com/playframework/play-silhouette 56 | [GitHub issue tracker]: https://github.com/playframework/play-silhouette/issues 57 | [GitHub workflow for submitting pull requests]: https://www.playframework.com/documentation/2.8.x/WorkingWithGit 58 | [chat]: https://discord.gg/g5s2vtZ4Fa 59 | [Scala Style Guide]: http://docs.scala-lang.org/style/ 60 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/impl/util/DefaultFingerprintGeneratorSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.util 17 | 18 | import play.silhouette.api.crypto.Hash 19 | import play.api.test.{ FakeRequest, PlaySpecification } 20 | 21 | /** 22 | * Test case for the [[play.silhouette.impl.util.DefaultFingerprintGenerator]] class. 23 | */ 24 | class DefaultFingerprintGeneratorSpec extends PlaySpecification { 25 | 26 | "The generator" should { 27 | "return fingerprint including the `User-Agent` header" in { 28 | val userAgent = "test-user-agent" 29 | val generator = new DefaultFingerprintGenerator() 30 | implicit val request = FakeRequest().withHeaders((USER_AGENT, userAgent)) 31 | 32 | generator.generate must be equalTo Hash.sha1(userAgent + ":::") 33 | } 34 | 35 | "return fingerprint including the `Accept-Language` header" in { 36 | val acceptLanguage = "test-accept-language" 37 | val generator = new DefaultFingerprintGenerator() 38 | implicit val request = FakeRequest().withHeaders((ACCEPT_LANGUAGE, acceptLanguage)) 39 | 40 | generator.generate must be equalTo Hash.sha1(":" + acceptLanguage + "::") 41 | } 42 | 43 | "return fingerprint including the `Accept-Charset` header" in { 44 | val acceptCharset = "test-accept-charset" 45 | val generator = new DefaultFingerprintGenerator() 46 | implicit val request = FakeRequest().withHeaders((ACCEPT_CHARSET, acceptCharset)) 47 | 48 | generator.generate must be equalTo Hash.sha1("::" + acceptCharset + ":") 49 | } 50 | 51 | "return fingerprint including the remote address" in { 52 | val generator = new DefaultFingerprintGenerator(true) 53 | implicit val request = FakeRequest() 54 | 55 | generator.generate must be equalTo Hash.sha1(":::127.0.0.1") 56 | } 57 | 58 | "return fingerprint including all values" in { 59 | val userAgent = "test-user-agent" 60 | val acceptLanguage = "test-accept-language" 61 | val acceptCharset = "test-accept-charset" 62 | val generator = new DefaultFingerprintGenerator(true) 63 | implicit val request = FakeRequest().withHeaders( 64 | (USER_AGENT, userAgent), 65 | (ACCEPT_LANGUAGE, acceptLanguage), 66 | (ACCEPT_CHARSET, acceptCharset)) 67 | 68 | generator.generate must be equalTo Hash.sha1( 69 | userAgent + ":" + acceptLanguage + ":" + acceptCharset + ":127.0.0.1") 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/impl/providers/openid/SteamProviderSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.providers.openid 17 | 18 | import play.silhouette.api.LoginInfo 19 | import play.silhouette.impl.providers._ 20 | import play.api.test.WithApplication 21 | import org.mockito.Mockito._ 22 | 23 | /** 24 | * Test case for the [[SteamProvider]] class. 25 | */ 26 | class SteamProviderSpec extends OpenIDProviderSpec { 27 | 28 | "The `withSettings` method" should { 29 | "create a new instance with customized settings" in new WithApplication with Context { 30 | override def running() = { 31 | val overrideSettingsFunction: OpenIDSettings => OpenIDSettings = { s => 32 | s.copy("new-provider-url") 33 | } 34 | val s: SteamProvider = provider.withSettings(overrideSettingsFunction) 35 | 36 | s.settings.providerURL must be equalTo "new-provider-url" 37 | verify(openIDService).withSettings(overrideSettingsFunction) 38 | } 39 | } 40 | } 41 | 42 | "The `retrieveProfile` method" should { 43 | "return the social profile" in new WithApplication with Context { 44 | override def running() = { 45 | profile(provider.retrieveProfile(openIDInfo)) { 46 | case p => p must be equalTo new CommonSocialProfile( 47 | loginInfo = LoginInfo(provider.id, "http://steamcommunity.com/openid/id/16261495063738643")) 48 | } 49 | } 50 | } 51 | } 52 | 53 | /** 54 | * Defines the context for the abstract OpenID provider spec. 55 | * 56 | * @return The Context to use for the abstract OpenID provider spec. 57 | */ 58 | override protected def context: OpenIDProviderSpecContext = new Context {} 59 | 60 | /** 61 | * The context. 62 | */ 63 | trait Context extends OpenIDProviderSpecContext { 64 | 65 | /** 66 | * A OpenID info. 67 | */ 68 | override lazy val openIDInfo = OpenIDInfo("http://steamcommunity.com/openid/id/16261495063738643", Map()) 69 | 70 | /** 71 | * The OpenID settings. 72 | */ 73 | lazy val openIDSettings = spy(OpenIDSettings( 74 | providerURL = "https://steamcommunity.com/openid/", 75 | callbackURL = "http://localhost:9000/authenticate/steam", 76 | realm = Some("http://localhost:9000"))) 77 | 78 | /** 79 | * The provider to test. 80 | */ 81 | lazy val provider: SteamProvider = new SteamProvider(httpLayer, openIDService, openIDSettings) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/repositories/AuthInfoRepository.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api.repositories 17 | 18 | import play.silhouette.api.{ AuthInfo, LoginInfo } 19 | 20 | import scala.concurrent.Future 21 | import scala.reflect.ClassTag 22 | 23 | /** 24 | * A trait that provides the means to persist authentication information for the Silhouette module. 25 | * 26 | * If the application supports the concept of "merged identities", i.e., the same user being 27 | * able to authenticate through different providers, then make sure that the auth info for 28 | * every linked login info gets stored separately. 29 | */ 30 | trait AuthInfoRepository { 31 | 32 | /** 33 | * Finds the auth info which is linked with the specified login info. 34 | * 35 | * @param loginInfo The linked login info. 36 | * @param tag The class tag of the auth info. 37 | * @tparam T The type of the auth info to handle. 38 | * @return The found auth info or None if no auth info could be found for the given login info. 39 | */ 40 | def find[T <: AuthInfo](loginInfo: LoginInfo)(implicit tag: ClassTag[T]): Future[Option[T]] 41 | 42 | /** 43 | * Adds new auth info for the given login info. 44 | * 45 | * @param loginInfo The login info for which the auth info should be saved. 46 | * @param authInfo The auth info to save. 47 | * @tparam T The type of the auth info to handle. 48 | * @return The saved auth info. 49 | */ 50 | def add[T <: AuthInfo](loginInfo: LoginInfo, authInfo: T): Future[T] 51 | 52 | /** 53 | * Updates the auth info for the given login info. 54 | * 55 | * @param loginInfo The login info for which the auth info should be updated. 56 | * @param authInfo The auth info to update. 57 | * @tparam T The type of the auth info to handle. 58 | * @return The updated auth info. 59 | */ 60 | def update[T <: AuthInfo](loginInfo: LoginInfo, authInfo: T): Future[T] 61 | 62 | /** 63 | * Saves the auth info for the given login info. 64 | * 65 | * This method either adds the auth info if it doesn't exists or it updates the auth info 66 | * if it already exists. 67 | * 68 | * @param loginInfo The login info for which the auth info should be saved. 69 | * @param authInfo The auth info to save. 70 | * @tparam T The type of the auth info to handle. 71 | * @return The updated auth info. 72 | */ 73 | def save[T <: AuthInfo](loginInfo: LoginInfo, authInfo: T): Future[T] 74 | 75 | /** 76 | * Removes the auth info for the given login info. 77 | * 78 | * @param loginInfo The login info for which the auth info should be removed. 79 | * @param tag The class tag of the auth info. 80 | * @tparam T The type of the auth info to handle. 81 | * @return A future to wait for the process to be completed. 82 | */ 83 | def remove[T <: AuthInfo](loginInfo: LoginInfo)(implicit tag: ClassTag[T]): Future[Unit] 84 | } 85 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/api/Silhouette.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api 17 | 18 | import jakarta.inject.Inject 19 | 20 | import play.silhouette.api.actions._ 21 | import play.api.mvc.AnyContent 22 | 23 | /** 24 | * The Silhouette stack. 25 | * 26 | * Inject an instance of this trait into your controller to provide all the Silhouette actions. 27 | * 28 | * @tparam E The type of the environment. 29 | */ 30 | trait Silhouette[E <: Env] { 31 | 32 | /** 33 | * The Silhouette environment. 34 | */ 35 | val env: Environment[E] 36 | 37 | /** 38 | * The secured action stack. 39 | */ 40 | val securedAction: SecuredAction 41 | 42 | /** 43 | * The unsecured action stack. 44 | */ 45 | val unsecuredAction: UnsecuredAction 46 | 47 | /** 48 | * The user aware action stack. 49 | */ 50 | val userAwareAction: UserAwareAction 51 | 52 | /** 53 | * Provides the secured action implementation. 54 | * 55 | * @return The secured action implementation. 56 | */ 57 | def SecuredAction: SecuredActionBuilder[E, AnyContent] = securedAction(env) 58 | 59 | /** 60 | * Provides the secured request handler implementation. 61 | * 62 | * @return The secured request handler implementation. 63 | */ 64 | def SecuredRequestHandler: SecuredRequestHandlerBuilder[E] = securedAction.requestHandler(env) 65 | 66 | /** 67 | * Provides the unsecured action implementation. 68 | * 69 | * @return The unsecured action implementation. 70 | */ 71 | def UnsecuredAction: UnsecuredActionBuilder[E, AnyContent] = unsecuredAction(env) 72 | 73 | /** 74 | * Provides the unsecured request handler implementation. 75 | * 76 | * @return The unsecured request handler implementation. 77 | */ 78 | def UnsecuredRequestHandler: UnsecuredRequestHandlerBuilder[E] = unsecuredAction.requestHandler(env) 79 | 80 | /** 81 | * Provides the user-aware action implementation. 82 | * 83 | * @return The user-aware action implementation. 84 | */ 85 | def UserAwareAction: UserAwareActionBuilder[E, AnyContent] = userAwareAction(env) 86 | 87 | /** 88 | * Provides the user-aware request handler implementation. 89 | * 90 | * @return The user-aware request handler implementation. 91 | */ 92 | def UserAwareRequestHandler: UserAwareRequestHandlerBuilder[E] = userAwareAction.requestHandler(env) 93 | } 94 | 95 | /** 96 | * Provides the Silhouette stack. 97 | * 98 | * @param env The Silhouette environment. 99 | * @param securedAction The secured action stack. 100 | * @param userAwareAction The user aware action stack. 101 | * @tparam E The type of the environment. 102 | */ 103 | class SilhouetteProvider[E <: Env] @Inject() ( 104 | val env: Environment[E], 105 | val securedAction: SecuredAction, 106 | val unsecuredAction: UnsecuredAction, 107 | val userAwareAction: UserAwareAction) extends Silhouette[E] 108 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/impl/providers/SocialProviderRegistrySpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.providers 17 | 18 | import play.silhouette.impl.providers.oauth1.TwitterProvider 19 | import play.silhouette.impl.providers.oauth2.{ GoogleProvider, FacebookProvider } 20 | import play.silhouette.impl.providers.openid.YahooProvider 21 | import org.specs2.specification.Scope 22 | import play.api.test.PlaySpecification 23 | import org.mockito.Mockito._ 24 | import test.Helper.mock 25 | 26 | /** 27 | * Test case for the [[play.silhouette.impl.providers.SocialProviderRegistry]] class. 28 | */ 29 | class SocialProviderRegistrySpec extends PlaySpecification { 30 | 31 | "The `get` method" should { 32 | "return a provider by its type" in new Context { 33 | registry.get[GoogleProvider] must beSome(providers(1)) 34 | } 35 | 36 | "return None if no provider for the given type exists" in new Context { 37 | registry.get[YahooProvider] must beNone 38 | } 39 | 40 | "return a provider by its ID as SocialProvider" in new Context { 41 | val provider = registry.get[SocialProvider](GoogleProvider.ID) 42 | 43 | provider must beSome[SocialProvider].like { 44 | case value => 45 | value.id must be equalTo providers(1).id 46 | value must beAnInstanceOf[SocialProvider] 47 | } 48 | } 49 | 50 | "return a provider by its ID as OAuth2Provider" in new Context { 51 | val provider = registry.get[OAuth2Provider](GoogleProvider.ID) 52 | 53 | provider must beSome[OAuth2Provider].like { 54 | case value => 55 | value.id must be equalTo providers(1).id 56 | value must beAnInstanceOf[OAuth2Provider] 57 | } 58 | } 59 | 60 | "return None if no provider for the given ID exists" in new Context { 61 | registry.get[SocialProvider](YahooProvider.ID) must beNone 62 | } 63 | } 64 | 65 | "The `getSeq` method" should { 66 | "return a list of providers by it's sub type" in new Context { 67 | val list = registry.getSeq[OAuth2Provider] 68 | list.head.id must be equalTo providers.head.id 69 | list(1).id must be equalTo providers(1).id 70 | } 71 | } 72 | 73 | /** 74 | * The context. 75 | */ 76 | trait Context extends Scope { 77 | 78 | /** 79 | * Some social providers. 80 | */ 81 | val providers = { 82 | val facebook = mock[FacebookProvider] 83 | when(facebook.id).thenReturn(FacebookProvider.ID) 84 | val google = mock[GoogleProvider] 85 | when(google.id).thenReturn(GoogleProvider.ID) 86 | val twitter = mock[TwitterProvider] 87 | when(twitter.id).thenReturn(TwitterProvider.ID) 88 | 89 | Seq( 90 | facebook, 91 | google, 92 | twitter) 93 | } 94 | 95 | /** 96 | * The registry to test. 97 | */ 98 | val registry = SocialProviderRegistry(providers) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/providers/openid/services/PlayOpenIDService.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: SecureSocial (https://github.com/jaliss/securesocial) 3 | * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss 4 | * 5 | * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) 6 | * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package play.silhouette.impl.providers.openid.services 21 | 22 | import play.silhouette.impl.providers.{ OpenIDInfo, OpenIDService, OpenIDSettings } 23 | import play.api.libs.openid.OpenIdClient 24 | import play.api.mvc.Request 25 | 26 | import scala.concurrent.{ ExecutionContext, Future } 27 | import scala.util.{ Failure, Success, Try } 28 | 29 | /** 30 | * The OpenID service implementation which wraps Play Framework's OpenID implementation. 31 | * 32 | * @param client The OpenID client implementation. 33 | * @param settings The OpenID settings. 34 | */ 35 | class PlayOpenIDService(val client: OpenIdClient, val settings: OpenIDSettings) extends OpenIDService { 36 | 37 | /** 38 | * The type of this class. 39 | */ 40 | override type Self = PlayOpenIDService 41 | 42 | /** 43 | * Retrieve the URL where the user should be redirected to start the OpenID authentication process. 44 | * 45 | * @param openID The OpenID to use for authentication. 46 | * @param resolvedCallbackURL The full callback URL to the application after a successful authentication. 47 | * @param ec The execution context to handle the asynchronous operations. 48 | * @return The redirect URL where the user should be redirected to start the OpenID authentication process. 49 | */ 50 | override def redirectURL(openID: String, resolvedCallbackURL: String)(implicit ec: ExecutionContext): Future[String] = { 51 | Try { 52 | client.redirectURL(openID, resolvedCallbackURL, settings.axRequired.toSeq, settings.axOptional.toSeq, settings.realm) 53 | } match { 54 | case Success(f) => f 55 | case Failure(e) => Future.failed(e) 56 | } 57 | } 58 | 59 | /** 60 | * From a request corresponding to the callback from the OpenID server, check the identity of the current user. 61 | * 62 | * @param request The current request. 63 | * @param ec The execution context to handle the asynchronous operations. 64 | * @tparam B The type of the request body. 65 | * @return A OpenIDInfo in case of success, Exception otherwise. 66 | */ 67 | override def verifiedID[B](implicit request: Request[B], ec: ExecutionContext) = Try { 68 | client.verifiedId(request).map(info => OpenIDInfo(info.id, info.attributes)) 69 | } match { 70 | case Success(f) => f 71 | case Failure(e) => Future.failed(e) 72 | } 73 | 74 | /** 75 | * Gets a service initialized with a new settings object. 76 | * 77 | * @param f A function which gets the settings passed and returns different settings. 78 | * @return An instance of the service initialized with new settings. 79 | */ 80 | override def withSettings(f: (OpenIDSettings) => OpenIDSettings) = { 81 | new PlayOpenIDService(client, f(settings)) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/impl/providers/openid/YahooProviderSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.providers.openid 17 | 18 | import play.silhouette.api.LoginInfo 19 | import play.silhouette.impl.providers._ 20 | import play.api.test.WithApplication 21 | import org.mockito.Mockito._ 22 | 23 | /** 24 | * Test case for the [[YahooProvider]] class. 25 | */ 26 | class YahooProviderSpec extends OpenIDProviderSpec { 27 | 28 | "The `withSettings` method" should { 29 | "create a new instance with customized settings" in new WithApplication with Context { 30 | override def running() = { 31 | val overrideSettingsFunction: OpenIDSettings => OpenIDSettings = { s => 32 | s.copy("new-provider-url") 33 | } 34 | val s: YahooProvider = provider.withSettings(overrideSettingsFunction) 35 | 36 | s.settings.providerURL must be equalTo "new-provider-url" 37 | verify(openIDService).withSettings(overrideSettingsFunction) 38 | } 39 | } 40 | } 41 | 42 | "The `retrieveProfile` method" should { 43 | "return the social profile" in new WithApplication with Context { 44 | override def running() = { 45 | profile(provider.retrieveProfile(openIDInfo)) { 46 | case p => 47 | p must be equalTo new CommonSocialProfile( 48 | loginInfo = LoginInfo(provider.id, "https://me.yahoo.com/a/Xs6hPjazdrMvmbn4jhQjkjkhcasdGdsKajq9we"), 49 | fullName = Some("Apollonia Vanova"), 50 | email = Some("apollonia.vanova@watchmen.com"), 51 | avatarURL = Some("https://s.yimg.com/dh/ap/social/profile/profile_b48.png")) 52 | } 53 | } 54 | } 55 | } 56 | 57 | /** 58 | * Defines the context for the abstract OpenID provider spec. 59 | * 60 | * @return The Context to use for the abstract OpenID provider spec. 61 | */ 62 | override protected def context: OpenIDProviderSpecContext = new Context {} 63 | 64 | /** 65 | * The context. 66 | */ 67 | trait Context extends OpenIDProviderSpecContext { 68 | 69 | /** 70 | * A OpenID info. 71 | */ 72 | override lazy val openIDInfo = OpenIDInfo("https://me.yahoo.com/a/Xs6hPjazdrMvmbn4jhQjkjkhcasdGdsKajq9we", Map( 73 | "fullname" -> "Apollonia Vanova", 74 | "email" -> "apollonia.vanova@watchmen.com", 75 | "image" -> "https://s.yimg.com/dh/ap/social/profile/profile_b48.png")) 76 | 77 | /** 78 | * The OpenID settings. 79 | */ 80 | lazy val openIDSettings = spy(OpenIDSettings( 81 | providerURL = "https://me.yahoo.com/", 82 | callbackURL = "http://localhost:9000/authenticate/yahoo", 83 | axRequired = Map( 84 | "fullname" -> "http://axschema.org/namePerson", 85 | "email" -> "http://axschema.org/contact/email", 86 | "image" -> "http://axschema.org/media/image/default"), 87 | realm = Some("http://localhost:9000"))) 88 | 89 | /** 90 | * The provider to test. 91 | */ 92 | lazy val provider: YahooProvider = new YahooProvider(httpLayer, openIDService, openIDSettings) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /silhouette/app-2/play/silhouette/api/Environment.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.api 17 | 18 | import play.silhouette.api.services.{ AuthenticatorService, IdentityService } 19 | import play.silhouette.api.util.ExecutionContextProvider 20 | 21 | import scala.concurrent.ExecutionContext 22 | 23 | /** 24 | * The environment type. 25 | * 26 | * Defines the [[Identity]] and [[Authenticator]] types for an environment. It is possible 27 | * to implement as many types as needed. This has the advantage that an application isn't 28 | * bound only to a single `Identity` -> `Authenticator` combination. 29 | * 30 | * To define a new environment type create a new trait with the appropriate [[Identity]] and 31 | * [[Authenticator]] types: 32 | * 33 | * {{{ 34 | * trait SessionEnv extends Env { 35 | * type I = User 36 | * type A = SessionAuthenticator 37 | * } 38 | * trait JWTEnv extends Env { 39 | * type I = User 40 | * type A = JWTAuthenticator 41 | * } 42 | * }}} 43 | */ 44 | trait Env { 45 | type I <: Identity 46 | type A <: Authenticator 47 | } 48 | 49 | /** 50 | * Provides the components needed to handle a secured request. 51 | * 52 | * It's possible to declare different environments for different environment types. The 53 | * [[play.silhouette.api.services.IdentityService]] and the 54 | * [[play.silhouette.api.services.AuthenticatorService]] are bound to the appropriate types 55 | * defined in the environment type. But the [[EventBus]] and the list of [[RequestProvider]] 56 | * instances can be defined as needed for every environment type. 57 | */ 58 | trait Environment[E <: Env] extends ExecutionContextProvider { 59 | 60 | /** 61 | * Gets the identity service implementation. 62 | * 63 | * @return The identity service implementation. 64 | */ 65 | def identityService: IdentityService[E#I] 66 | 67 | /** 68 | * Gets the authenticator service implementation. 69 | * 70 | * @return The authenticator service implementation. 71 | */ 72 | def authenticatorService: AuthenticatorService[E#A] 73 | 74 | /** 75 | * Gets the list of request providers. 76 | * 77 | * @return The list of request providers. 78 | */ 79 | def requestProviders: Seq[RequestProvider] 80 | 81 | /** 82 | * The event bus implementation. 83 | * 84 | * @return The event bus implementation. 85 | */ 86 | def eventBus: EventBus 87 | } 88 | 89 | /** 90 | * Companion object to easily create environment instances. 91 | * 92 | * {{{ 93 | * Environment[SessionEnv](...) 94 | * Environment[JWTEnv](...) 95 | * }}} 96 | */ 97 | object Environment { 98 | def apply[E <: Env]( 99 | identityServiceImpl: IdentityService[E#I], 100 | authenticatorServiceImpl: AuthenticatorService[E#A], 101 | requestProvidersImpl: Seq[RequestProvider], 102 | eventBusImpl: EventBus)(implicit ec: ExecutionContext) = new Environment[E] { 103 | val identityService = identityServiceImpl 104 | val authenticatorService = authenticatorServiceImpl 105 | val requestProviders = requestProvidersImpl 106 | val eventBus = eventBusImpl 107 | val executionContext = ec 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/providers/openid/SteamProvider.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.providers.openid 17 | 18 | import play.silhouette.api.LoginInfo 19 | import play.silhouette.api.util.HTTPLayer 20 | import play.silhouette.impl.providers._ 21 | import play.silhouette.impl.providers.openid.SteamProvider._ 22 | 23 | import scala.concurrent.Future 24 | 25 | /** 26 | * Base Steam OpenID Provider. 27 | * 28 | * @see https://steamcommunity.com/dev 29 | */ 30 | trait BaseSteamProvider extends OpenIDProvider { 31 | 32 | /** 33 | * The content type to parse a profile from. 34 | */ 35 | override type Content = Unit 36 | 37 | /** 38 | * Gets the provider ID. 39 | * 40 | * @return The provider ID. 41 | */ 42 | override val id: String = ID 43 | 44 | /** 45 | * Defines the URLs that are needed to retrieve the profile data. 46 | */ 47 | override protected val urls: Map[String, String] = Map.empty 48 | 49 | /** 50 | * Builds the social profile. 51 | * 52 | * @param authInfo The auth info received from the provider. 53 | * @return On success the build social profile, otherwise a failure. 54 | */ 55 | override protected def buildProfile(authInfo: OpenIDInfo): Future[Profile] = { 56 | profileParser.parse((), authInfo) 57 | } 58 | } 59 | 60 | /** 61 | * The profile parser for the common social profile. 62 | */ 63 | class SteamProfileParser extends SocialProfileParser[Unit, CommonSocialProfile, OpenIDInfo] { 64 | 65 | /** 66 | * Parses the social profile. 67 | * 68 | * @param authInfo The auth info received from the provider. 69 | * @return The social profile from given result. 70 | */ 71 | override def parse(data: Unit, authInfo: OpenIDInfo): Future[CommonSocialProfile] = Future.successful { 72 | CommonSocialProfile(loginInfo = LoginInfo(ID, authInfo.id)) 73 | } 74 | } 75 | 76 | /** 77 | * The Steam OAuth2 Provider. 78 | * 79 | * @param httpLayer The HTTP layer implementation. 80 | * @param service The OpenID service implementation. 81 | * @param settings The OpenID provider settings. 82 | */ 83 | class SteamProvider( 84 | protected val httpLayer: HTTPLayer, 85 | val service: OpenIDService, 86 | val settings: OpenIDSettings) 87 | extends BaseSteamProvider with CommonSocialProfileBuilder { 88 | 89 | /** 90 | * The type of this class. 91 | */ 92 | override type Self = SteamProvider 93 | 94 | /** 95 | * The profile parser implementation. 96 | */ 97 | override val profileParser = new SteamProfileParser 98 | 99 | /** 100 | * Gets a provider initialized with a new settings object. 101 | * 102 | * @param f A function which gets the settings passed and returns different settings. 103 | * @return An instance of the provider initialized with new settings. 104 | */ 105 | override def withSettings(f: Settings => Settings): SteamProvider = { 106 | new SteamProvider(httpLayer, service.withSettings(f), f(settings)) 107 | } 108 | } 109 | 110 | /** 111 | * The companion object. 112 | */ 113 | object SteamProvider { 114 | 115 | /** 116 | * The Steam constants. 117 | */ 118 | val ID = "steam" 119 | } 120 | -------------------------------------------------------------------------------- /silhouette-persistence/src/main/scala/play/silhouette/persistence/daos/InMemoryAuthInfoDAO.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.persistence.daos 17 | 18 | import play.silhouette.api.{ AuthInfo, LoginInfo } 19 | 20 | import scala.collection.mutable 21 | import scala.concurrent.ExecutionContext.Implicits.global 22 | import scala.concurrent.Future 23 | import scala.reflect.ClassTag 24 | 25 | /** 26 | * An implementation of the auth info DAO which stores the data in memory. 27 | * 28 | * This is not thread-safe implementation which should only be used for testing or development purpose. 29 | * 30 | * @param classTag The class tag for the type parameter. 31 | * @tparam T The type of the auth info to store. 32 | */ 33 | class InMemoryAuthInfoDAO[T <: AuthInfo](implicit val classTag: ClassTag[T]) extends DelegableAuthInfoDAO[T] { 34 | 35 | /** 36 | * The data store for the auth info. 37 | */ 38 | var data: mutable.HashMap[LoginInfo, T] = mutable.HashMap() 39 | 40 | /** 41 | * Finds the auth info which is linked with the specified login info. 42 | * 43 | * @param loginInfo The linked login info. 44 | * @return The retrieved auth info or None if no auth info could be retrieved for the given login info. 45 | */ 46 | def find(loginInfo: LoginInfo): Future[Option[T]] = { 47 | Future.successful(data.get(loginInfo)) 48 | } 49 | 50 | /** 51 | * Adds new auth info for the given login info. 52 | * 53 | * @param loginInfo The login info for which the auth info should be added. 54 | * @param authInfo The auth info to add. 55 | * @return The added auth info. 56 | */ 57 | def add(loginInfo: LoginInfo, authInfo: T): Future[T] = { 58 | data += (loginInfo -> authInfo) 59 | Future.successful(authInfo) 60 | } 61 | 62 | /** 63 | * Updates the auth info for the given login info. 64 | * 65 | * @param loginInfo The login info for which the auth info should be updated. 66 | * @param authInfo The auth info to update. 67 | * @return The updated auth info. 68 | */ 69 | def update(loginInfo: LoginInfo, authInfo: T): Future[T] = { 70 | data += (loginInfo -> authInfo) 71 | Future.successful(authInfo) 72 | } 73 | 74 | /** 75 | * Saves the auth info for the given login info. 76 | * 77 | * This method either adds the auth info if it doesn't exists or it updates the auth info 78 | * if it already exists. 79 | * 80 | * @param loginInfo The login info for which the auth info should be saved. 81 | * @param authInfo The auth info to save. 82 | * @return The saved auth info. 83 | */ 84 | def save(loginInfo: LoginInfo, authInfo: T): Future[T] = { 85 | find(loginInfo).flatMap { 86 | case Some(_) => update(loginInfo, authInfo) 87 | case None => add(loginInfo, authInfo) 88 | } 89 | } 90 | 91 | /** 92 | * Removes the auth info for the given login info. 93 | * 94 | * @param loginInfo The login info for which the auth info should be removed. 95 | * @return A future to wait for the process to be completed. 96 | */ 97 | def remove(loginInfo: LoginInfo): Future[Unit] = { 98 | data -= loginInfo 99 | Future.successful(()) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /silhouette-persistence/src/test/scala/play/silhouette/persistence/repositories/CacheAuthenticatorRepositorySpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.persistence.repositories 17 | 18 | import play.silhouette.api.StorableAuthenticator 19 | import play.silhouette.api.util.CacheLayer 20 | import play.silhouette.test.WaitPatience 21 | import org.specs2.concurrent.ExecutionEnv 22 | import org.specs2.mutable.Specification 23 | import org.specs2.specification.Scope 24 | import org.mockito.Mockito._ 25 | 26 | import scala.concurrent.Future 27 | import scala.concurrent.duration.Duration 28 | 29 | /** 30 | * Test case for the [[CacheAuthenticatorRepository]] class. 31 | */ 32 | class CacheAuthenticatorRepositorySpec(implicit ev: ExecutionEnv) extends Specification with WaitPatience { 33 | 34 | "The `find` method" should { 35 | "return value from cache" in new Context { 36 | when(cacheLayer.find[StorableAuthenticator]("test-id")).thenReturn(Future.successful(Some(authenticator))) 37 | 38 | repository.find("test-id") must beSome(authenticator).awaitWithPatience 39 | verify(cacheLayer).find[StorableAuthenticator]("test-id") 40 | } 41 | 42 | "return None if value couldn't be found in cache" in new Context { 43 | when(cacheLayer.find[StorableAuthenticator]("test-id")).thenReturn(Future.successful(None)) 44 | 45 | repository.find("test-id") must beNone.awaitWithPatience 46 | verify(cacheLayer).find[StorableAuthenticator]("test-id") 47 | } 48 | } 49 | 50 | "The `add` method" should { 51 | "add value in cache" in new Context { 52 | when(authenticator.id).thenReturn("test-id") 53 | when(cacheLayer.save("test-id", authenticator, Duration.Inf)).thenReturn(Future.successful(authenticator)) 54 | 55 | repository.add(authenticator) must beEqualTo(authenticator).awaitWithPatience 56 | verify(cacheLayer).save("test-id", authenticator, Duration.Inf) 57 | } 58 | } 59 | 60 | "The `update` method" should { 61 | "update value in cache" in new Context { 62 | when(authenticator.id).thenReturn("test-id") 63 | when(cacheLayer.save("test-id", authenticator, Duration.Inf)).thenReturn(Future.successful(authenticator)) 64 | 65 | repository.update(authenticator) must beEqualTo(authenticator).awaitWithPatience 66 | verify(cacheLayer).save("test-id", authenticator, Duration.Inf) 67 | } 68 | } 69 | 70 | "The `remove` method" should { 71 | "remove value from cache" in new Context { 72 | when(cacheLayer.remove("test-id")).thenReturn(Future.successful(())) 73 | 74 | repository.remove("test-id") must beEqualTo(()).awaitWithPatience 75 | verify(cacheLayer).remove("test-id") 76 | } 77 | } 78 | 79 | /** 80 | * The context. 81 | */ 82 | trait Context extends Scope { 83 | 84 | /** 85 | * A storable authenticator. 86 | */ 87 | lazy val authenticator = mock(classOf[StorableAuthenticator]) 88 | 89 | /** 90 | * The cache layer implementation. 91 | */ 92 | lazy val cacheLayer = mock(classOf[CacheLayer]) 93 | 94 | /** 95 | * The repository to test. 96 | */ 97 | lazy val repository = new CacheAuthenticatorRepository[StorableAuthenticator](cacheLayer) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /silhouette-persistence/src/test/scala/play/silhouette/persistence/daos/InMemoryAuthInfoDAOSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.persistence.daos 17 | 18 | import play.silhouette.api.{ AuthInfo, LoginInfo } 19 | import play.silhouette.test.WaitPatience 20 | import org.specs2.concurrent.ExecutionEnv 21 | import org.specs2.mutable.Specification 22 | import org.specs2.specification.Scope 23 | 24 | import scala.concurrent.Await 25 | import scala.concurrent.duration._ 26 | import scala.language.postfixOps 27 | 28 | /** 29 | * Test case for the [[InMemoryAuthInfoDAO]] trait. 30 | */ 31 | class InMemoryAuthInfoDAOSpec(implicit ev: ExecutionEnv) extends Specification with WaitPatience { 32 | 33 | "The `find` method" should { 34 | "find an OAuth1 info for the given login info" in new Context { 35 | Await.result(dao.save(loginInfo, authInfo), 10 seconds) 36 | 37 | dao.find(loginInfo) must beSome(authInfo).awaitWithPatience 38 | } 39 | 40 | "return None if no OAuth1 info for the given login info exists" in new Context { 41 | dao.find(loginInfo.copy(providerKey = "new.key")) should beNone.awaitWithPatience 42 | } 43 | } 44 | 45 | "The `add` method" should { 46 | "add a new OAuth1 info" in new Context { 47 | dao.add(loginInfo.copy(providerKey = "new.key"), authInfo) must beEqualTo(authInfo).awaitWithPatience 48 | dao.find(loginInfo.copy(providerKey = "new.key")) must beSome(authInfo).awaitWithPatience 49 | } 50 | } 51 | 52 | "The `update` method" should { 53 | "update an existing OAuth1 info" in new Context { 54 | val updatedInfo = authInfo.copy(data = "updated") 55 | 56 | dao.update(loginInfo, updatedInfo) must beEqualTo(updatedInfo).awaitWithPatience 57 | dao.find(loginInfo) must beSome(updatedInfo).awaitWithPatience 58 | } 59 | } 60 | 61 | "The `save` method" should { 62 | "insert a new OAuth1 info" in new Context { 63 | dao.save(loginInfo.copy(providerKey = "new.key"), authInfo) must beEqualTo(authInfo).awaitWithPatience 64 | dao.find(loginInfo.copy(providerKey = "new.key")) must beSome(authInfo).awaitWithPatience 65 | } 66 | 67 | "update an existing OAuth1 info" in new Context { 68 | val updatedInfo = authInfo.copy(data = "updated") 69 | 70 | dao.update(loginInfo, updatedInfo) must beEqualTo(updatedInfo).awaitWithPatience 71 | dao.find(loginInfo) must beSome(updatedInfo).awaitWithPatience 72 | } 73 | } 74 | 75 | "The `remove` method" should { 76 | "remove an OAuth1 info" in new Context { 77 | Await.result(dao.remove(loginInfo), 10 seconds) 78 | dao.find(loginInfo) must beNone.awaitWithPatience 79 | } 80 | } 81 | 82 | /** 83 | * The context. 84 | */ 85 | trait Context extends Scope { 86 | 87 | /** 88 | * A test auth info implementation. 89 | */ 90 | case class TestInfo(data: String) extends AuthInfo 91 | 92 | /** 93 | * The OAuth1 info DAO implementation. 94 | */ 95 | lazy val dao = new InMemoryAuthInfoDAO[TestInfo] {} 96 | 97 | /** 98 | * A login info. 99 | */ 100 | lazy val loginInfo = LoginInfo("provider", "6253282") 101 | 102 | /** 103 | * A OAuth1 info. 104 | */ 105 | lazy val authInfo = TestInfo("my.data") 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /silhouette/test/play/silhouette/impl/providers/state/UserStateItemHandlerSpec.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.providers.state 17 | 18 | import play.silhouette.impl.providers.SocialStateItem 19 | import play.silhouette.impl.providers.SocialStateItem.ItemStructure 20 | import play.silhouette.impl.providers.state.UserStateItemHandler._ 21 | import org.specs2.matcher.JsonMatchers 22 | import org.specs2.specification.Scope 23 | import play.api.libs.json.Json 24 | import play.api.mvc.AnyContentAsEmpty 25 | import play.api.test.{ FakeRequest, PlaySpecification } 26 | import org.mockito.Mockito._ 27 | import test.Helper.mockSmart 28 | 29 | import scala.concurrent.ExecutionContext.Implicits.global 30 | 31 | /** 32 | * Test case for the [[UserStateItemHandler]] class. 33 | */ 34 | class UserStateItemHandlerSpec extends PlaySpecification with JsonMatchers { 35 | 36 | "The `item` method" should { 37 | "return the user state item" in new Context { 38 | await(userStateItemHandler.item) must be equalTo userStateItem 39 | } 40 | } 41 | 42 | "The `canHandle` method" should { 43 | "return the same item if it can handle the given item" in new Context { 44 | userStateItemHandler.canHandle(userStateItem) must beSome(userStateItem) 45 | } 46 | 47 | "should return `None` if it can't handle the given item" in new Context { 48 | val nonUserState = mockSmart[SocialStateItem] 49 | 50 | userStateItemHandler.canHandle(nonUserState) must beNone 51 | } 52 | } 53 | 54 | "The `canHandle` method" should { 55 | "return false if the give item is for another handler" in new Context { 56 | val nonUserItemStructure = mockSmart[ItemStructure] 57 | when(nonUserItemStructure.id).thenReturn("non-user-item") 58 | 59 | implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() 60 | userStateItemHandler.canHandle(nonUserItemStructure) must beFalse 61 | } 62 | 63 | "return true if it can handle the given `ItemStructure`" in new Context { 64 | implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() 65 | userStateItemHandler.canHandle(userItemStructure) must beTrue 66 | } 67 | } 68 | 69 | "The `serialize` method" should { 70 | "return a serialized value of the state item" in new Context { 71 | userStateItemHandler.serialize(userStateItem).asString must be equalTo userItemStructure.asString 72 | } 73 | } 74 | 75 | "The `unserialize` method" should { 76 | "unserialize the state item" in new Context { 77 | implicit val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest() 78 | 79 | await(userStateItemHandler.unserialize(userItemStructure)) must be equalTo userStateItem 80 | } 81 | } 82 | 83 | /** 84 | * The context. 85 | */ 86 | trait Context extends Scope { 87 | 88 | /** 89 | * A user state item. 90 | */ 91 | val userStateItem = UserStateItem(Map("path" -> "/login")) 92 | 93 | /** 94 | * The serialized type of the user state item. 95 | */ 96 | val userItemStructure = ItemStructure(ID, Json.toJson(userStateItem)) 97 | 98 | /** 99 | * An instance of the user state item handler. 100 | */ 101 | val userStateItemHandler = new UserStateItemHandler[UserStateItem](userStateItem) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /silhouette/app/play/silhouette/impl/providers/openid/YahooProvider.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015 Mohiva Organisation (license at mohiva dot com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package play.silhouette.impl.providers.openid 17 | 18 | import play.silhouette.api.LoginInfo 19 | import play.silhouette.api.util.HTTPLayer 20 | import play.silhouette.impl.providers._ 21 | import play.silhouette.impl.providers.openid.YahooProvider._ 22 | 23 | import scala.concurrent.Future 24 | 25 | /** 26 | * Base Yahoo OpenID Provider. 27 | * 28 | * @see https://developer.yahoo.com/openid/index.html 29 | */ 30 | trait BaseYahooProvider extends OpenIDProvider { 31 | 32 | /** 33 | * The content type to parse a profile from. 34 | */ 35 | override type Content = Unit 36 | 37 | /** 38 | * Gets the provider ID. 39 | * 40 | * @return The provider ID. 41 | */ 42 | override val id: String = ID 43 | 44 | /** 45 | * Defines the URLs that are needed to retrieve the profile data. 46 | */ 47 | override protected val urls: Map[String, String] = Map.empty 48 | 49 | /** 50 | * Builds the social profile. 51 | * 52 | * @param authInfo The auth info received from the provider. 53 | * @return On success the build social profile, otherwise a failure. 54 | */ 55 | override protected def buildProfile(authInfo: OpenIDInfo): Future[Profile] = { 56 | profileParser.parse((), authInfo) 57 | } 58 | } 59 | 60 | /** 61 | * The profile parser for the common social profile. 62 | */ 63 | class YahooProfileParser extends SocialProfileParser[Unit, CommonSocialProfile, OpenIDInfo] { 64 | 65 | /** 66 | * Parses the social profile. 67 | * 68 | * @param authInfo The auth info received from the provider. 69 | * @return The social profile from given result. 70 | */ 71 | override def parse(data: Unit, authInfo: OpenIDInfo): Future[CommonSocialProfile] = Future.successful { 72 | CommonSocialProfile( 73 | loginInfo = LoginInfo(ID, authInfo.id), 74 | fullName = authInfo.attributes.get("fullname"), 75 | email = authInfo.attributes.get("email"), 76 | avatarURL = authInfo.attributes.get("image")) 77 | } 78 | } 79 | 80 | /** 81 | * The Yahoo OAuth2 Provider. 82 | * 83 | * @param httpLayer The HTTP layer implementation. 84 | * @param service The OpenID service implementation. 85 | * @param settings The OpenID provider settings. 86 | */ 87 | class YahooProvider( 88 | protected val httpLayer: HTTPLayer, 89 | val service: OpenIDService, 90 | val settings: OpenIDSettings) 91 | extends BaseYahooProvider with CommonSocialProfileBuilder { 92 | 93 | /** 94 | * The type of this class. 95 | */ 96 | override type Self = YahooProvider 97 | 98 | /** 99 | * The profile parser implementation. 100 | */ 101 | override val profileParser = new YahooProfileParser 102 | 103 | /** 104 | * Gets a provider initialized with a new settings object. 105 | * 106 | * @param f A function which gets the settings passed and returns different settings. 107 | * @return An instance of the provider initialized with new settings. 108 | */ 109 | override def withSettings(f: Settings => Settings): YahooProvider = { 110 | new YahooProvider(httpLayer, service.withSettings(f), f(settings)) 111 | } 112 | } 113 | 114 | /** 115 | * The companion object. 116 | */ 117 | object YahooProvider { 118 | 119 | /** 120 | * The Yahoo constants. 121 | */ 122 | val ID = "yahoo" 123 | } 124 | -------------------------------------------------------------------------------- /silhouette-password-bcrypt/src/main/scala/play/silhouette/password/BCryptSha256PasswordHasher.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: SecureSocial (https://github.com/jaliss/securesocial) 3 | * Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss 4 | * 5 | * Derivative work: Silhouette (https://github.com/mohiva/play-silhouette) 6 | * Modifications Copyright 2015 Mohiva Organisation (license at mohiva dot com) 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | package play.silhouette.password 21 | 22 | import play.silhouette.api.crypto.Hash 23 | import play.silhouette.api.util.PasswordInfo 24 | import play.silhouette.password.BCryptSha256PasswordHasher._ 25 | import org.mindrot.jbcrypt.BCrypt 26 | 27 | /** 28 | * Implementation of the password hasher based on BCrypt. 29 | * 30 | * The designers of bcrypt truncate all passwords at 72 characters which means that `bcrypt(password_with_100_chars) == 31 | * bcrypt(password_with_100_chars[:72])`. The original `BCryptPasswordHasher` does not have any special handling and 32 | * thus is also subject to this hidden password length limit. `BCryptSha256PasswordHasher` fixes this by first hashing 33 | * the password using sha256. This prevents the password truncation and so should be preferred over the 34 | * `BCryptPasswordHasher`. The practical ramification of this truncation is pretty marginal as the average user does 35 | * not have a password greater than 72 characters in length and even being truncated at 72 the compute powered required 36 | * to brute force bcrypt in any useful amount of time is still astronomical. Nonetheless, we recommend you use 37 | * `BCryptSha256PasswordHasher` anyway on the principle of "better safe than sorry". 38 | * 39 | * @param logRounds The log2 of the number of rounds of hashing to apply. 40 | * @see [[http://www.mindrot.org/files/jBCrypt/jBCrypt-0.2-doc/BCrypt.html#gensalt(int) gensalt]] 41 | * @see https://docs.djangoproject.com/en/1.10/topics/auth/passwords/#using-bcrypt-with-django 42 | * @see https://crypto.stackexchange.com/questions/24993/is-there-a-way-to-use-bcrypt-with-passwords-longer-than-72-bytes-securely 43 | */ 44 | class BCryptSha256PasswordHasher(logRounds: Int = 10) extends BCryptPasswordHasher(logRounds) { 45 | 46 | /** 47 | * Gets the ID of the hasher. 48 | * 49 | * @return The ID of the hasher. 50 | */ 51 | override def id = ID 52 | 53 | /** 54 | * Hashes a password. 55 | * 56 | * This implementation does not return the salt separately because it is embedded in the hashed password. 57 | * Other implementations might need to return it so it gets saved in the backing store. 58 | * 59 | * @param plainPassword The password to hash. 60 | * @return A PasswordInfo containing the hashed password. 61 | */ 62 | override def hash(plainPassword: String) = PasswordInfo( 63 | hasher = id, 64 | password = BCrypt.hashpw(Hash.sha2(plainPassword), BCrypt.gensalt(logRounds))) 65 | 66 | /** 67 | * Checks if a password matches the hashed version. 68 | * 69 | * @param passwordInfo The password retrieved from the backing store. 70 | * @param suppliedPassword The password supplied by the user trying to log in. 71 | * @return True if the password matches, false otherwise. 72 | */ 73 | override def matches(passwordInfo: PasswordInfo, suppliedPassword: String) = { 74 | BCrypt.checkpw(Hash.sha2(suppliedPassword), passwordInfo.password) 75 | } 76 | } 77 | 78 | /** 79 | * The companion object. 80 | */ 81 | object BCryptSha256PasswordHasher { 82 | 83 | /** 84 | * The ID of the hasher. 85 | */ 86 | val ID = "bcrypt-sha256" 87 | } 88 | --------------------------------------------------------------------------------