Skip to content

Instantly share code, notes, and snippets.

@leon
Created March 12, 2013 07:55
Show Gist options
  • Save leon/5141012 to your computer and use it in GitHub Desktop.
Save leon/5141012 to your computer and use it in GitHub Desktop.
Secure Social UserService Slick implementation (Not Working)
package service
import play.api._
import securesocial.core._
import securesocial.core.providers.Token
import securesocial.core.UserId
import models._
class SlickUserService(application: Application) extends UserServicePlugin(application) {
def find(id: UserId): Option[Identity] = Users.findByUserId(id)
def findByEmailAndProvider(email: String, providerId: String): Option[Identity] = Users.findByEmailAndProvider(email, providerId)
def save(identity: Identity): Identity = Users.save(User.fromIdentity(identity))
def save(token: Token) { Tokens.save(token) }
def findToken(token: String): Option[Token] = Tokens.findByUUID(token)
def deleteToken(uuid: String) { Tokens.delete(uuid) }
def deleteTokens() { Tokens.deleteAll() }
def deleteExpiredTokens() { Tokens.deleteExpiredTokens() }
}
package models
import play.api.db.slick.DB
import play.api.db.slick.Config.driver.simple._
import securesocial.core._
import securesocial.core.providers.Token
import _root_.java.sql.Date
import org.joda.time.DateTime
import play.api.Play.current
/*
Slick Table for securesocial.core.providers.Token
case class Token(
uuid: String,
email: String,
creationTime: org.joda.time.DateTime,
expirationTime: org.joda.time.DateTime,
isSignUp: Boolean
)
*/
object Tokens extends Table[Token]("tokens") {
// Conversions for JodaTime
implicit def date2dateTime = MappedTypeMapper.base[DateTime, Date] (
dateTime => new Date(dateTime.getMillis),
date => new DateTime(date)
)
def uuid = column[String]("uuid", O.PrimaryKey)
def email = column[String]("email")
def creationTime = column[DateTime]("creationTime")
def expirationTime = column[DateTime]("expirationTime")
def isSignUp = column[Boolean]("isSignUp")
// Projections
def * = {
uuid ~
email ~
creationTime ~
expirationTime ~
isSignUp <> (Token.apply _, Token.unapply _)
}
// Operations
def save(token: Token): Token = DB.withTransaction { implicit session =>
findByUUID(token.uuid) map { t =>
Query(Tokens).where(_.uuid is t.uuid).update(token)
} getOrElse {
this.insert(token)
}
token
}
def delete(uuid: String) = DB.withTransaction { implicit session =>
this.where(_.uuid is uuid).mutate(_.delete)
}
def deleteAll() = DB.withTransaction { implicit session =>
Query(Tokens).mutate(_.delete)
}
def deleteExpiredTokens() = DB.withTransaction { implicit session =>
Query(Tokens).where(_.expirationTime <= DateTime.now).mutate(_.delete)
}
// Queries
def all: List[User] = DB.withSession { implicit session =>
val q = for (user <- Users) yield user
q.list
}
def findByUUID(uuid: String): Option[Token] = DB.withSession { implicit session =>
def byUUID = createFinderBy(_.uuid)
byUUID(uuid).firstOption
}
}
package models
import play.api.libs.Codecs
import play.api.db.slick.DB
import play.api.db.slick.Config.driver.simple._
import securesocial.core._
import play.api.Play.current
case class User(
pid: Option[Long] = None,
userId: String,
providerId: String,
email: Option[String],
firstName: String,
lastName: String,
authMethod: AuthenticationMethod,
oAuth1Info: Option[OAuth1Info] = None,
oAuth2Info: Option[OAuth2Info] = None,
passwordInfo: Option[PasswordInfo] = None
) extends Identity {
def id: UserId = UserId(userId, providerId)
def fullName: String = s"$firstName $lastName"
def avatarUrl: Option[String] = email.map { e => s"http://www.gravatar.com/avatar/${Codecs.md5(e.getBytes)}.png" }
}
object User {
def fromIdentity(user: Identity) = {
User(
pid = None,
userId = user.id.id,
providerId = user.id.providerId,
email = user.email,
firstName = user.firstName,
lastName = user.lastName,
authMethod = user.authMethod,
oAuth1Info = user.oAuth1Info,
oAuth2Info = user.oAuth2Info,
passwordInfo = user.passwordInfo
)
}
}
object Users extends Table[User]("users") {
// Conversions for AuthenticationMethod
implicit def string2AuthenticationMethod = MappedTypeMapper.base[AuthenticationMethod, String] (
authenticationMethod => authenticationMethod.method,
string => AuthenticationMethod(string)
)
def pid = column[Long]("id", O.PrimaryKey, O.AutoInc)
def userId = column[String]("userId")
def providerId = column[String]("providerId")
def email = column[Option[String]]("email")
def firstName = column[String]("firstName")
def lastName = column[String]("lastName")
def authMethod = column[AuthenticationMethod]("authMethod")
def oAuth1Info = {
def token = column[String]("token")
def secret = column[String]("secret")
token ~ secret <> (OAuth1Info.apply _, OAuth1Info.unapply _)
}
def oAuth2Info = {
def accessToken = column[String]("accessToken")
def tokenType = column[Option[String]]("tokenType")
def expiresIn = column[Option[Int]]("expiresIn")
def refreshToken = column[Option[String]]("refreshToken")
accessToken ~ tokenType ~ expiresIn ~ refreshToken <> (OAuth2Info.apply _, OAuth2Info.unapply _)
}
def passwordInfo = {
def hasher = column[String]("hasher")
def password = column[String]("password")
def salt = column[Option[String]]("salt")
hasher ~ password ~ salt <> (PasswordInfo.apply _, PasswordInfo.unapply _)
}
// Projections
def * = {
pid.? ~
userId ~
providerId ~
email ~
firstName ~
lastName ~
authMethod ~
oAuth1Info.? ~
oAuth2Info.? ~
passwordInfo.? <> (User.apply _, User.unapply _)
}
def autoInc = * returning pid
// Operations
def save(user: User): User = DB.withTransaction { implicit session =>
user.pid match {
case None | Some(0) => {
val pid = this.autoInc.insert(user)
user.copy(pid = Some(pid))
}
case Some(pid) => {
Query(Users).where(_.pid is pid).update(user)
user
}
}
}
def delete(pid: Long) = DB.withTransaction { implicit session =>
this.where(_.pid is pid).mutate(_.delete)
}
// Queries
def all: List[User] = DB.withSession { implicit session =>
val q = for (user <- Users) yield user
q.list
}
def findById(pid: Long): Option[User] = DB.withSession { implicit session =>
def byId = createFinderBy(_.pid)
byId(pid).firstOption
}
def findByEmail(email: String): Option[User] = DB.withSession { implicit session =>
def byEmail = createFinderBy(_.email)
byEmail(email).firstOption
}
def findByUserId(userId: UserId): Option[User] = DB.withSession { implicit session =>
val q = for {
user <- this if (this.userId is userId.id) && (this.providerId is userId.providerId)
} yield user
q.firstOption
}
def findByEmailAndProvider(email: String, providerId: String): Option[User] = DB.withSession { implicit session =>
val q = for {
user <- this if (this.email is email) && (this.providerId is providerId)
} yield user
q.firstOption
}
}
@Wirwing
Copy link

Wirwing commented Jan 24, 2014

What's the current status of this gist? Not working yet?

@Damiya
Copy link

Damiya commented Jan 29, 2014

@Wirwing I've got a working version that uses an extended User object with a Long primary-key UID.

other than that it's fully compliant using implicit conversions to/from JSON Strings in the table.

https://github.com/Damiya/legendary/tree/master/app

@nelsonblaha
Copy link

@damyIA Is it still available? That link 404s.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment