Last active
January 3, 2016 20:29
-
-
Save strobe/8515423 to your computer and use it in GitHub Desktop.
Simple Play 2, Slick 1.x UserService for SecureSocial
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
files locations: | |
app/models/Users.scala | |
app/service/InSlickUserService.scala | |
conf/play.plugins | |
build.sbt |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ... | |
libraryDependencies += "com.github.tototoshi" %% "slick-joda-mapper" % "0.4.0" | |
// Autorization Secure Social- http://securesocial.ws/ | |
resolvers += Resolver.url("sbt-plugin-releases", new URL("http://repo.scala-sbt.org/scalasbt/sbt-plugin-releases"))(Resolver.ivyStylePatterns) | |
libraryDependencies += "securesocial" %% "securesocial" % "2.1.2" | |
// ... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package service | |
import play.api.{Logger, Application} | |
import securesocial.core._ | |
import securesocial.core.providers.Token | |
import models._ | |
import models.UserIdentity | |
import models.UserToken | |
import scala.Some | |
// Use H2Driver to connect to an H2 database | |
import play.api.db.slick.Config.driver.simple._ | |
import play.api.Play.current | |
import com.github.tototoshi.slick.JodaSupport._ | |
/** | |
* Slick - postgresql oriented User Service implementation | |
*/ | |
class InSlickUserService(application: Application) extends UserServicePlugin(application) { | |
/** | |
* Finds a SocialUser that maches the specified id | |
*/ | |
def find(id: IdentityId):Option[Identity] = { | |
var found: Option[Identity] = None | |
play.api.db.slick.DB.withSession { implicit session:Session => | |
val users = for { | |
u <- UserIdentities if u.userId === id.userId && u.providerId === id.providerId | |
} yield u | |
found = users.firstOption match { | |
case Some(user) => { | |
Some(SocialUser(IdentityId(user.userId, user.providerId), | |
user.firstName, | |
user.lastName, | |
user.fullName, | |
user.email, | |
user.avatarUrl, | |
user.authMethod, | |
user.oAuth1Info, | |
Some(OAuth2Info(user.accessToken, user.tokenType, user.expiresIn, user.refreshToken)), | |
Some(PasswordInfo(user.hasher, user.password, user.salt)) | |
)) | |
} | |
case _ => None | |
} | |
} | |
found | |
} | |
/** | |
* Finds a Social user by email and provider id. | |
*/ | |
def findByEmailAndProvider(email: String, providerId: String):Option[Identity] = | |
{ | |
var found: Option[Identity] = None | |
play.api.db.slick.DB.withSession { implicit session:Session => | |
val users = for { | |
u <- UserIdentities if u.email === email && u.providerId === providerId | |
} yield u | |
found = users.firstOption match { | |
case Some(user) => { | |
Some(SocialUser(IdentityId(user.userId, user.providerId), | |
user.firstName, | |
user.lastName, | |
user.fullName, | |
user.email, | |
user.avatarUrl, | |
user.authMethod, | |
user.oAuth1Info, | |
Some(OAuth2Info(user.accessToken, user.tokenType, user.expiresIn, user.refreshToken)), | |
Some(PasswordInfo(user.hasher, user.password, user.salt)) | |
)) | |
} | |
case _ => None | |
} | |
} | |
found | |
} | |
/** | |
* Saves the user. This method gets called when a user logs in. | |
*/ | |
def save(user: Identity): Identity = { | |
play.api.db.slick.DB.withSession { implicit session:Session => | |
// Some((accT: String, tT: Option[String], expIn: Option[Int], refT: Option[String])), // oAuth2Info | |
val oA2i: OAuth2Info = user.oAuth2Info.getOrElse(OAuth2Info("", None, None, None)) | |
val passwi: PasswordInfo = user.passwordInfo.getOrElse(PasswordInfo("", "", None)) | |
UserIdentities.forInsert insert UserIdentity( | |
None, | |
user.identityId.userId, | |
user.identityId.providerId, | |
user.firstName, | |
user.lastName, | |
user.fullName, | |
user.email, | |
user.avatarUrl, | |
user.authMethod, | |
user.oAuth1Info, | |
oA2i.accessToken, oA2i.tokenType, oA2i.expiresIn, oA2i.refreshToken, | |
passwi.hasher, passwi.password, passwi.salt) | |
} | |
user | |
} | |
/** | |
* Saves a token. This is needed for users that | |
* are creating an account in the system instead of using one in a 3rd party | |
* system. | |
*/ | |
def save(token: Token) = { | |
play.api.db.slick.DB.withSession { implicit session:Session => | |
UserTokens.forInsert insert UserToken(None, token.uuid, token.email, | |
token.creationTime, token.expirationTime, | |
token.isSignUp) | |
} | |
} | |
/** | |
* Finds a token | |
*/ | |
def findToken(token: String): Option[Token] = { | |
var r: Option[Token] = None | |
play.api.db.slick.DB.withSession { implicit session:Session => | |
val toks = for { | |
t <- UserTokens if t.uuid === token | |
} yield t | |
r = toks.firstOption match { | |
case Some(tok) => Some(Token(tok.uuid, tok.email, tok.creationTime, tok.expirationTime, tok.isSignUp)) | |
case _ => None | |
} | |
} | |
r | |
} | |
/** | |
* Deletes a token | |
*/ | |
def deleteToken(uuid: String) { | |
play.api.db.slick.DB.withSession { implicit session:Session => | |
val q = Query(UserTokens).filter(_.uuid === uuid) | |
q.delete | |
q.deleteInvoker | |
} | |
} | |
/** | |
* Deletes all expired tokens | |
*/ | |
def deleteExpiredTokens() { | |
play.api.db.slick.DB.withSession { implicit session:Session => | |
val q = Query(UserTokens).filter(_.expirationTime < org.joda.time.DateTime.now() ) | |
q.delete | |
q.deleteInvoker | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1500:com.typesafe.plugin.CommonsMailerPlugin | |
9994:securesocial.core.DefaultAuthenticatorStore | |
9995:securesocial.core.DefaultIdGenerator | |
9996:securesocial.core.providers.utils.DefaultPasswordValidator | |
9997:securesocial.controllers.DefaultTemplatesPlugin | |
9998:service.InSlickUserService #InMemoryUserService | |
9999:securesocial.core.providers.utils.BCryptPasswordHasher | |
#10000:securesocial.core.providers.TwitterProvider | |
#10001:securesocial.core.providers.FacebookProvider | |
#10002:securesocial.core.providers.GoogleProvider | |
#10003:securesocial.core.providers.LinkedInProvider | |
10004:securesocial.core.providers.UsernamePasswordProvider | |
#10005:securesocial.core.providers.GitHubProvider | |
#10006:securesocial.core.providers.FoursquareProvider | |
#10007:securesocial.core.providers.XingProvider | |
#10008:securesocial.core.providers.VkProvider | |
#10009:securesocial.core.providers.InstagramProvider |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package models | |
// Importing the slick driver | |
import play.api.db.slick.Config.driver.simple._ | |
import scala.slick.session.Session | |
import play.api.libs.json._ | |
import securesocial.core.{ IdentityId, OAuth1Info, AuthenticationMethod } | |
import org.joda.time.DateTime | |
import com.github.tototoshi.slick.JodaSupport._ | |
//- Secure Social Authorization -// | |
import securesocial.core.Identity | |
trait Authorization { | |
def isAuthorized(user: Identity): Boolean | |
} | |
case class WithProvider(provider: String) extends Authorization { | |
def isAuthorized(user: Identity) = { | |
user.identityId.providerId == provider | |
} | |
} | |
case class UserToken(tokenId: Option[Int] = None, | |
uuid: String, | |
email: String, | |
creationTime: org.joda.time.DateTime, | |
expirationTime: org.joda.time.DateTime, | |
isSignUp: Boolean) { | |
def isExpired = expirationTime.isBeforeNow | |
} | |
object UserTokens extends Table[UserToken]("USER_TOKENS") { | |
def tokenId = column[Int]("TOKEN_ID", O.PrimaryKey, O.AutoInc) | |
def uuid = column[String]("UUID", O.NotNull) | |
def email = column[String]("EMAIL", O.NotNull) | |
def creationTime = column[org.joda.time.DateTime]("CREATION_TIME", O.NotNull) | |
def expirationTime = column[org.joda.time.DateTime]("EXPIRATION_TIME", O.NotNull) | |
def isSignUp = column[Boolean]("IS_SIGNUP", O.NotNull) | |
def * = (tokenId.? ~ uuid ~ email ~ creationTime | |
~ expirationTime ~ isSignUp) <> (UserToken.apply _, UserToken.unapply _) | |
def forInsert = uuid ~ email ~ creationTime ~ expirationTime ~ isSignUp <> ( | |
{ t => UserToken(None, t._1, t._2, t._3, t._4, t._5) }, | |
{ u: UserToken => Some((u.uuid, u.email, u.creationTime, | |
u.expirationTime, u.isSignUp)) } | |
) | |
} | |
/** The case class to represent the User data object | |
* for storing SocialUser in database | |
*/ | |
case class UserIdentity(primaryId: Option[Int] = None, | |
userId: String, providerId: String, // IdentityId | |
firstName: String, | |
lastName: String, | |
fullName: String, | |
email: Option[String], | |
avatarUrl: Option[String], | |
authMethod: AuthenticationMethod, // AuthenticationMethod | |
oAuth1Info: Option[OAuth1Info], // OAuth1Info | |
accessToken: String, tokenType: Option[String], expiresIn: Option[Int], refreshToken: Option[String], // OAuth2Info | |
hasher: String, password: String, salt: Option[String]) // PasswordInfo | |
object UserIdentities extends Table[UserIdentity]("USER_IDENTITIES") { | |
private val sep: String = "@;@" | |
implicit val oAuth1InfoTypeMapper = MappedTypeMapper.base[OAuth1Info, String]( | |
{ oaInfo => oaInfo.token.toString + sep + oaInfo.secret.toString }, | |
{ s => | |
val r: Array[String] = s.split(sep) | |
OAuth1Info(r(0), r(1)) | |
} | |
) | |
implicit val authenticationMethodTypeMapper = | |
MappedTypeMapper.base[AuthenticationMethod, String]( | |
{ | |
case AuthenticationMethod.OAuth1 => "oauth1" | |
case AuthenticationMethod.OAuth2 => "oauth2" | |
case AuthenticationMethod.OpenId => "openId" | |
case AuthenticationMethod.UserPassword => "userPassword" | |
}, | |
{ | |
case "oauth1" => AuthenticationMethod.OAuth1 | |
case "oauth2" => AuthenticationMethod.OAuth2 | |
case "openId" => AuthenticationMethod.OpenId | |
case "userPassword" => AuthenticationMethod.UserPassword | |
} | |
) | |
def primaryId = column[Int]("ID", O.PrimaryKey, O.AutoInc) | |
def userId = column[String]("USER_ID", O.NotNull) | |
def providerId = column[String]("PROVIDER_ID", O.NotNull) | |
def firstName = column[String]("FIRST_NAME", O.NotNull) | |
def lastName = column[String]("LAST_NAME", O.NotNull) | |
def fullName = column[String]("FULL_NAME", O.NotNull) | |
def email = column[String]("EMAIL", O.NotNull) | |
def avatarUrl = column[String]("AVATAR_URL", O.Nullable) | |
def authMethod = column[AuthenticationMethod]("AUTH_METHOD", O.NotNull) | |
def oAuth1Info = column[OAuth1Info]("OAUTH1_INFO", O.Nullable) | |
// OAuth2Info | |
def accessToken = column[String]("ACCESS_TOKEN", O.NotNull) | |
def tokenType = column[String]("TOKEN_TYPE", O.Nullable) | |
def expiresIn = column[Int]("EXPIRES_IN", O.Nullable) | |
def refreshToken = column[String]("REFRESH_TOKEN", O.Nullable) | |
// PasswordInfo | |
def hasher = column[String]("HASHER", O.NotNull) | |
def password = column[String]("PASSWORD", O.NotNull) | |
def salt = column[String]("SALT", O.Nullable) | |
// Every table needs a * projection with the same type as the table's type parameter | |
override def * = | |
(primaryId.? ~ userId ~ providerId ~ firstName ~ lastName ~ fullName | |
~ email.? ~ avatarUrl.? ~ authMethod ~ oAuth1Info.? ~ accessToken | |
~ tokenType.? ~ expiresIn.? ~ refreshToken.? ~ hasher ~ password | |
~ salt.?) <> (UserIdentity.apply _, UserIdentity.unapply _) | |
def forInsert = (userId ~ providerId ~ firstName ~ lastName ~ fullName | |
~ email.? ~ avatarUrl.? ~ authMethod ~ oAuth1Info.? ~ accessToken | |
~ tokenType.? ~ expiresIn.? ~ refreshToken.? ~ hasher ~ password | |
~ salt.? <> ( | |
{ t => UserIdentity(None, t._1, t._2, t._3, t._4, t._5, t._6, t._7, t._8, | |
t._9, t._10, t._11, t._12, t._13, t._14, | |
t._15, t._16) }, | |
{ u: UserIdentity => Some((u.userId, u.providerId, u.firstName, | |
u.lastName, u.fullName, u.email, u.avatarUrl, | |
u.authMethod, u.oAuth1Info, u.accessToken, | |
u.tokenType, u.expiresIn, u.refreshToken, | |
u.hasher, u.password, u.salt )) } | |
)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment