Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Contribution of pgsql support for AuthenticatorStore and UserService - Compatible with Play! 2.1 version #163

Open
wants to merge 1 commit into from

2 participants

@akinsella

pgsql support for AuthenticatorStore provides persistent and clusterisable implementation.
Same for UserService. Pgsql support for UserService is based on Some(implementation) found on Play! mailing list, which was also submitted 3 month ago as a pull request.
SQL scripts are provided. Dependencies on anorm and jdbc are required.

@larvanitis

I am giving this code a try and I noticed you have some unnecessary/duplicate imports.

Also on PgSqlUserService.scala, you should replace user.passwordInfo.get.password with user.passwordInfo.map(_.password).getOrElse(None) because many social services won't have one and you'll get a null pointer exception

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 23, 2013
  1. @akinsella
This page is out of date. Refresh to see the latest.
View
178 module-code/app/securesocial/core/support.pgsql/PgSqlAuthenticatorStore.scala
@@ -0,0 +1,178 @@
+package securesocial.core.support.pgsql
+
+
+import _root_.java.util.Date
+import play.api.db._
+import anorm._
+
+import play.api.Play.current
+
+
+import play.api.{Logger, Application}
+import scala.Error
+import scala.Some
+
+import org.joda.time.DateTime
+import securesocial.core.{Authenticator, UserId, AuthenticatorStore}
+
+class PgSqlAuthenticatorStore(app: Application) extends AuthenticatorStore(app) {
+
+ // case class Authenticator(id: String, userId: UserId, creationDate: DateTime,
+ // lastUsed: DateTime, expirationDate: DateTime)
+
+
+ def save(authenticator: Authenticator): Either[Error, Unit] = {
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("Save authenticator [%s]".format(authenticator))
+ }
+
+ DB.withConnection { implicit c =>
+
+ val sqlSelectQuery = SQL(
+ """
+ SELECT * FROM authenticator WHERE id = {id};
+ """).on("id" -> authenticator.id)
+
+ val authenticators = sqlSelectQuery().map(row =>
+ Authenticator(
+ row[String]("id"),
+ UserId(row[String]("userId"), row[String]("provider")),
+ new DateTime(row[Date]("creationDate")),
+ new DateTime(row[Date]("lastUsed")),
+ new DateTime(row[Date]("expirationDate"))
+ )).toList
+
+ val foundAuthenticator = if (authenticators.size == 1) authenticators(0) else None
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("authenticator = %s".format(foundAuthenticator))
+ }
+
+ if (foundAuthenticator == None) { // user not exists
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("INSERT")
+ }
+
+ // create a new user
+ val sqlQuery = SQL(
+ """
+ INSERT INTO authenticator (id, userId, provider, creationDate, lastUsed, expirationDate)
+ VALUES ({id}, {userId}, {provider}, {creationDate}, {lastUsed}, {expirationDate})
+ """).on(
+ 'id -> authenticator.id,
+ 'userId -> authenticator.userId.id,
+ 'provider -> authenticator.userId.providerId,
+ 'creationDate -> authenticator.creationDate.toDate,
+ 'lastUsed -> authenticator.lastUsed.toDate,
+ 'expirationDate -> authenticator.expirationDate.toDate
+ )
+
+ val result: Int = sqlQuery.executeUpdate()
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("result = %s".format(result))
+ }
+
+ } else { // user exists
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("UPDATE")
+ }
+
+ // update the user
+ val sqlQuery = SQL(
+ """
+ UPDATE authenticator
+ SET id = {id},
+ userId = {userId},
+ provider = {provider},
+ creationDate = {creationDate},
+ lastUsed = {lastUsed},
+ expirationDate = {expirationDate}
+ WHERE id = {id}
+ """).on(
+ 'id -> authenticator.id,
+ 'userId -> authenticator.userId.id,
+ 'provider -> authenticator.userId.providerId,
+ 'creationDate -> authenticator.creationDate.toDate,
+ 'lastUsed -> authenticator.lastUsed.toDate,
+ 'expirationDate -> authenticator.expirationDate.toDate
+ )
+
+ val result: Int = sqlQuery.executeUpdate()
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("result = %s".format(result))
+ }
+
+ } // end else
+
+ authenticator
+ } // end DB
+
+ Right(())
+ } // end save
+
+ def find(id: String): Either[Error, Option[Authenticator]] = {
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("Find Authenticator with Id = '%s' ...".format(id))
+ }
+
+ DB.withConnection { implicit c =>
+
+ val sqlQuery = SQL(
+ """
+ SELECT * FROM authenticator WHERE id = {id};
+ """).on("id" -> id)
+
+ // Transform the resulting Stream[Row] to a List[Authenticators]
+ val authenticators = sqlQuery().map(row =>
+ Authenticator(
+ row[String]("id"),
+ UserId(row[String]("userId"), row[String]("provider")),
+ new DateTime(row[Date]("creationDate")),
+ new DateTime(row[Date]("lastUsed")),
+ new DateTime(row[Date]("expirationDate"))
+ )).toList
+
+ val authenticator = if (authenticators.size == 1) Some(authenticators(0)) else None
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("authenticator = %s".format(authenticator))
+ }
+
+ Right((authenticator))
+ } // end DB
+
+ } // end find
+
+ def delete(id: String): Either[Error, Unit] = {
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("delete authenticator...")
+ Logger.debug("Authenticator Id = %s".format(id))
+ }
+
+ DB.withConnection { implicit c =>
+
+ // delete token
+ val sqlQuery = SQL(
+ """
+ DELETE FROM authenticator WHERE id = {id};
+ """).on("id" -> id)
+
+ val result: Int = sqlQuery.executeUpdate()
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("result = %s".format(result))
+ }
+
+ } // end DB
+
+ Right(())
+ } // end delete user
+
+}
View
427 module-code/app/securesocial/core/support.pgsql/PgSqlUserService.scala
@@ -0,0 +1,427 @@
+/**
+ * Copyright 2012 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package securesocial.core.support.pgsql
+
+
+import _root_.java.util.{Date, UUID}
+
+import securesocial.core._
+import providers.Token
+import securesocial.core.UserId
+import securesocial.core.PasswordInfo
+import scala.Some
+
+import play.api.{Logger, Application}
+import securesocial.core._
+import org.joda.time.DateTime
+import securesocial.core.providers.Token
+import securesocial.core.providers.Token
+
+import play.api.db._
+import anorm._
+
+import play.api.Play.current
+import scala.Some
+
+
+/**
+ * A Database user service in Scala
+ *
+ */
+class PgSqlUserService(application: Application) extends UserServicePlugin(application) {
+
+
+ /**
+ * find user
+ *
+ */
+ def find(id: UserId) = {
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("find...")
+ Logger.debug("id = %s".format(id.id))
+ }
+
+ DB.withConnection { implicit c =>
+
+ val sqlQuery = SQL(
+ """
+ SELECT * FROM "user" WHERE id = {id};
+ """).on("id" -> id.id)
+
+ // Transform the resulting Stream[Row] to a List[SocialUser]
+ val users = sqlQuery().map(row =>
+ SocialUser(
+ UserId(row[String]("id"), row[String]("provider")),
+ row[String]("firstName"),
+ row[String]("lastName"),
+ row[String]("firstName") + " " + row[String]("lastName"),
+ row[Option[String]]("email"),
+ None,
+ AuthenticationMethod("userPassword"),
+ None,
+ None,
+ Some(PasswordInfo("bcrypt", row[String]("password"), None))
+ )).toList
+
+ val socialUser = if (users.size == 1) Some(users(0)) else None
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("socialUser = %s".format(socialUser))
+ }
+
+ socialUser
+
+ } // end DB
+
+ } // end find
+
+
+ /**
+ * findByEmailAndProvider user
+ *
+ */
+ def findByEmailAndProvider(email: String, providerId: String): Option[SocialUser] = {
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("findByEmailAndProvider...")
+ Logger.debug("email = %s".format(email))
+ Logger.debug("providerId = %s".format(providerId))
+ }
+
+ DB.withConnection { implicit c =>
+
+ val sqlQuery = SQL(
+ """
+ SELECT * FROM "user" WHERE email = {email} AND provider = {provider}
+ """).on(
+ 'email -> email,
+ 'provider -> providerId
+ )
+
+ // Transform the resulting Stream[Row] to a List[SocialUser]
+ val users = sqlQuery().map(row =>
+ SocialUser(
+ UserId(row[String]("id"), row[String]("provider")),
+ row[String]("firstName"),
+ row[String]("lastName"),
+ row[String]("firstName") + " " + row[String]("lastName"),
+ row[Option[String]]("email"),
+ None,
+ AuthenticationMethod("userPassword"),
+ None,
+ None,
+ Some(PasswordInfo("bcrypt", row[String]("password"), None))
+ )).toList
+
+ val socialUser = if (users.size == 1) Some(users(0)) else None
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("socialUser = %s".format(socialUser))
+ }
+
+ socialUser
+
+ } // end DB
+
+ } // end findByEmailAndProvider
+
+
+ /**
+ * save user
+ * (actually save or update)
+ *
+ */
+ def save(user: Identity):Identity = {
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("save...")
+ Logger.debug("user = %s".format(user))
+ }
+
+ DB.withConnection { implicit c =>
+
+ val sqlSelectQuery = SQL(
+ """
+ SELECT * FROM "user" WHERE id = {id};
+ """).on("id" -> user.id.id)
+
+ val users = sqlSelectQuery().map(row =>
+ SocialUser(
+ UserId(row[String]("id"), row[String]("provider")),
+ row[String]("firstName"),
+ row[String]("lastName"),
+ row[String]("firstName") + " " + row[String]("lastName"),
+ row[Option[String]]("email"),
+ None,
+ AuthenticationMethod("userPassword"),
+ None,
+ None,
+ Some(PasswordInfo("bcrypt", row[String]("password"), None))
+ )).toList
+
+ val socialUser = if (users.size == 1) Some(users(0)) else None
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("socialUser = %s".format(socialUser))
+ }
+
+ if (socialUser == None) { // user not exists
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("INSERT")
+ }
+
+ // create a new user
+ val sqlQuery = SQL(
+ """
+ INSERT INTO "user" (id, provider, firstName, lastName, email, "password")
+ VALUES ({id}, {provider}, {firstName}, {lastName}, {email}, {password})
+ """).on(
+ 'id -> user.id.id,
+ 'provider -> user.id.providerId,
+ 'firstName -> user.firstName,
+ 'lastName -> user.lastName,
+ 'email -> user.email,
+ 'password -> user.passwordInfo.get.password
+ )
+
+ val result: Int = sqlQuery.executeUpdate()
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("result = %s".format(result))
+ }
+
+ } else { // user exists
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("UPDATE")
+ }
+
+ // update the user
+ val sqlQuery = SQL(
+ """
+ UPDATE "user"
+ SET id = {id},
+ provider = {provider},
+ firstName = {firstName},
+ lastName = {lastName},
+ email = {email},
+ "password" = {password}
+ WHERE id = {id}
+ """).on(
+ 'id -> user.id.id,
+ 'provider -> user.id.providerId,
+ 'firstName -> user.firstName,
+ 'lastName -> user.lastName,
+ 'email -> user.email,
+ 'password -> user.passwordInfo.get.password
+ )
+
+ val result: Int = sqlQuery.executeUpdate()
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("result = %s".format(result))
+ }
+
+ } // end else
+
+ user
+ } // end DB
+
+ } // end save
+
+
+ /**
+ * save token
+ *
+ */
+ def save(token: Token) {
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("save...")
+ Logger.debug("token = %s".format(token))
+ }
+
+ DB.withConnection { implicit c =>
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("INSERT")
+ }
+
+ // create a new token
+ val sqlQuery = SQL(
+ """
+ INSERT INTO token (uuid, email, createdAt, expireAt, isSignUp)
+ VALUES ({uuid}, {email}, to_timestamp({createdAt}, 'YYYY-MM-DD HH24:MI:SS'), to_timestamp({expireAt}, 'YYYY-MM-DD HH24:MI:SS'), {isSignUp})
+ """).on(
+ 'uuid -> token.uuid,
+ 'email -> token.email,
+ 'createdAt -> token.creationTime.toString("yyyy-MM-dd HH:mm:ss"),
+ 'expireAt -> token.expirationTime.toString("yyyy-MM-dd HH:mm:ss"),
+ 'isSignUp -> token.isSignUp
+ )
+
+ val result: Int = sqlQuery.executeUpdate()
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("result = %s".format(result))
+ }
+
+ } // end DB
+
+ } // end save
+
+
+ /**
+ * findToken
+ *
+ */
+ def findToken(token: String): Option[Token] = {
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("findToken...")
+ Logger.debug("token = %s".format(token))
+ }
+
+ DB.withConnection { implicit c =>
+
+ val sqlSelectQuery = SQL(
+ """
+ SELECT * FROM token
+ WHERE uuid = {uuid};
+ """).on("uuid" -> token)
+
+ val tokens = sqlSelectQuery().map(row => {
+ val creationTime = row[Date]("createdAt")
+ val expirationTime = row[Date]("expireAt")
+ if (Logger.isDebugEnabled) {
+ Logger.debug("creationTime = %s".format(creationTime))
+ Logger.debug("expirationTime = %s".format(expirationTime))
+ }
+ Token(
+ row[String]("uuid"),
+ row[String]("email"),
+ new DateTime(creationTime),
+ new DateTime(expirationTime),
+ row[Boolean]("isSignUp")
+ )
+ }).toList
+
+ val foundToken = if (tokens.size == 1) Some(tokens(0)) else None
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("foundToken = %s".format(foundToken))
+ }
+
+ foundToken
+
+ } // end DB
+
+ } // end findToken
+
+
+ /**
+ * deleteToken
+ *
+ */
+ def deleteToken(uuid: String) {
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("deleteToken...")
+ Logger.debug("uuid = %s".format(uuid))
+ }
+
+ DB.withConnection { implicit c =>
+
+ // delete token
+ val sqlQuery = SQL(
+ """
+ DELETE FROM token WHERE uuid = {uuid};
+ """).on("uuid" -> uuid)
+
+ val result: Int = sqlQuery.executeUpdate()
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("result = %s".format(result))
+ }
+
+ } // end DB
+
+ } // end deleteToken
+
+
+ /**
+ * deleteTokens
+ *
+ */
+ def deleteTokens() {
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("deleteTokens...")
+ }
+
+ DB.withConnection { implicit c =>
+
+ // delete all tokens
+ val sqlQuery = SQL(
+ """
+ DELETE FROM token;
+ """)
+
+ val result: Int = sqlQuery.executeUpdate()
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("result = %s".format(result))
+ }
+
+ } // end DB
+
+ } // end deleteTokens
+
+
+ /**
+ * deleteExpiredTokens
+ *
+ */
+ def deleteExpiredTokens() {
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("deleteExpiredTokens...")
+ }
+
+ DB.withConnection { implicit c =>
+
+ // delete expired tokens
+ val sqlQuery = SQL(
+ """
+ DELETE FROM token
+ WHERE expireAt < current_timestamp;
+ """)
+
+ val result: Int = sqlQuery.executeUpdate()
+
+ if (Logger.isDebugEnabled) {
+ Logger.debug("result = %s".format(result))
+ }
+
+ } // end DB
+
+ } // end deleteExpiredTokens
+
+
+} // end DbUserService
View
109 module-code/app/securesocial/core/support/memory/InMemoryUserService.scala
@@ -0,0 +1,109 @@
+package securesocial.core.support.memory
+
+import play.api.{Logger, Application}
+import securesocial.core._
+import securesocial.core.providers.Token
+import securesocial.core.UserId
+import scala.Some
+import org.mindrot.jbcrypt.BCrypt
+
+class InMemoryUserService(application: Application) extends UserServicePlugin(application) {
+
+ private var users = Map[String, Identity]()
+ private var tokens = Map[String, Token]()
+
+ /**
+ * Finds a user that maches the specified id
+ *
+ * @param id the user id
+ * @return an optional user
+ */
+ def find(id: UserId):Option[Identity] = {
+ if ( Logger.isDebugEnabled ) {
+ Logger.debug("users = %s".format(users))
+ }
+ users.get(id.id + id.providerId)
+ }
+
+ /**
+ * Finds a user by email and provider id.
+ *
+ * Note: If you do not plan to use the UsernamePassword provider just provide en empty
+ * implementation.
+ *
+ * @param email - the user email
+ * @param providerId - the provider id
+ * @return
+ */
+ def findByEmailAndProvider(email: String, providerId: String):Option[Identity] = {
+ if ( Logger.isDebugEnabled ) {
+ Logger.debug("users = %s".format(users))
+ }
+ users.values.find( u => u.email.map( e => e == email && u.id.providerId == providerId).getOrElse(false))
+ }
+
+ /**
+ * Saves the user. This method gets called when a user logs in.
+ * This is your chance to save the user information in your backing store.
+ * @param user
+ */
+ def save(user: Identity):Identity = {
+ users = users + (user.id.id + user.id.providerId -> user)
+ 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.
+ *
+ * Note: If you do not plan to use the UsernamePassword provider just provide en empty
+ * implementation
+ *
+ * @param token The token to save
+ * @return A string with a uuid that will be embedded in the welcome email.
+ */
+ def save(token: Token) {
+ tokens += (token.uuid -> token)
+ }
+
+
+ /**
+ * Finds a token
+ *
+ * Note: If you do not plan to use the UsernamePassword provider just provide en empty
+ * implementation
+ *
+ * @param token the token id
+ * @return
+ */
+ def findToken(token: String): Option[Token] = {
+ tokens.get(token)
+ }
+
+ /**
+ * Deletes a token
+ *
+ * Note: If you do not plan to use the UsernamePassword provider just provide en empty
+ * implementation
+ *
+ * @param uuid the token id
+ */
+ def deleteToken(uuid: String) {
+ tokens -= uuid
+ }
+
+ def deleteTokens() {
+ tokens = Map()
+ }
+
+ /**
+ * Deletes all expired tokens
+ *
+ * Note: If you do not plan to use the UsernamePassword provider just provide en empty
+ * implementation
+ *
+ */
+ def deleteExpiredTokens() {
+ tokens = tokens.filter(!_._2.isExpired)
+ }
+}
View
22 module-code/conf/evolutions/default/1.sql
@@ -0,0 +1,22 @@
+# --- !Ups
+
+CREATE TABLE "user" (
+ id VARCHAR(100) NOT NULL,
+ provider VARCHAR(100),
+ firstName VARCHAR(200),
+ lastName VARCHAR(200),
+ email VARCHAR(100),
+ "password" VARCHAR(100)
+);
+CREATE TABLE token (
+ uuid VARCHAR(100) NOT NULL PRIMARY KEY,
+ email VARCHAR(100),
+ createdAt TIMESTAMP,
+ expireAt TIMESTAMP,
+ isSignUp BOOLEAN
+);
+
+# --- !Downs
+
+DROP TABLE token;
+DROP TABLE "user";
View
15 module-code/conf/evolutions/default/2.sql
@@ -0,0 +1,15 @@
+# --- !Ups
+
+CREATE TABLE authenticator (
+ id VARCHAR(512) NOT NULL,
+ userId VARCHAR(128) NOT NULL,
+ provider VARCHAR(64),
+ creationDate timestamp NOT NULL,
+ lastUsed timestamp not null,
+ expirationDate timestamp not null,
+ updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
+);
+
+# --- !Downs
+
+DROP TABLE authenticator;
View
1  module-code/project/Build.scala
@@ -8,6 +8,7 @@ object ApplicationBuild extends Build {
val appVersion = "master-SNAPSHOT"
val appDependencies = Seq(
+ jdbc, anorm,
"com.typesafe" %% "play-plugins-util" % "2.1.0",
"com.typesafe" %% "play-plugins-mailer" % "2.1.0",
"org.mindrot" % "jbcrypt" % "0.3m"
Something went wrong with that request. Please try again.