Skip to content

Instantly share code, notes, and snippets.

@strobe
Last active January 3, 2016 20:29
Show Gist options
  • Save strobe/8515423 to your computer and use it in GitHub Desktop.
Save strobe/8515423 to your computer and use it in GitHub Desktop.
Simple Play 2, Slick 1.x UserService for SecureSocial
files locations:
app/models/Users.scala
app/service/InSlickUserService.scala
conf/play.plugins
build.sbt
// ...
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"
// ...
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
}
}
}
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
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