Skip to content

Commit

Permalink
Changed that The Play Session might not be used
Browse files Browse the repository at this point in the history
  • Loading branch information
gakuzzzz committed Nov 22, 2012
1 parent d170c00 commit 1dc399b
Show file tree
Hide file tree
Showing 13 changed files with 176 additions and 149 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ tmp
.history
.idea
.idea_modules
*.iml
.project
.settings/
.ivy2/
.classpath
9 changes: 4 additions & 5 deletions app/controllers/Application.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ object Application extends Controller with LoginLogout with AuthConfigImpl {
}

def logout = Action { implicit request =>
gotoLogoutSucceeded.flashing(
"success" -> "You've been logged out"
)
gotoLogoutSucceeded// .flashing(
// "success" -> "You've been logged out"
// )
}

def authenticate = Action { implicit request =>
Expand Down Expand Up @@ -86,8 +86,7 @@ trait AuthConfigImpl extends AuthConfig {
case _ => false
}

// override def resolver[A](implicit request: Request[A]) =
// new CookieRelationResolver[Id, A](request)
// override def idContainer = new CookieIdContainer[Id]

}

Expand Down
14 changes: 7 additions & 7 deletions module/src/main/scala/jp/t2v/lab/play20/auth/Auth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,31 @@ trait Auth {
def authorizedAction(authority: Authority)(f: User => Request[AnyContent] => Result): Action[AnyContent] =
authorizedAction(BodyParsers.parse.anyContent, authority)(f)

def authorizedAction[A](p: BodyParser[A], authority: Authority)(f: User => Request[A] => Result): Action[A] =
def authorizedAction[A](p: BodyParser[A], authority: Authority)(f: User => Request[A] => Result): Action[A] =
Action(BodyParser(req => authorized(authority)(req) match {
case Right(_) => p(req)
case Left(result) => Done(Left(result), Input.Empty)
})) { req =>
authorized(authority)(req).right.map(u => f(u)(req)).merge
}
}

def optionalUserAction(f: Option[User] => Request[AnyContent] => Result): Action[AnyContent] =
optionalUserAction(BodyParsers.parse.anyContent)(f)

def optionalUserAction[A](p: BodyParser[A])(f: Option[User] => Request[A] => Result): Action[A] =
Action(p)(req => f(restoreUser(req))(req))

def authorized(authority: Authority)(implicit request: RequestHeader): Either[PlainResult, User] = for {
def authorized(authority: Authority)(implicit request: RequestHeader): Either[Result, User] = for {
user <- restoreUser(request).toRight(authenticationFailed(request)).right
_ <- Either.cond(authorize(user, authority), (), authorizationFailed(request)).right
} yield user

private def restoreUser(implicit request: RequestHeader): Option[User] = for {
sessionId <- request.session.get("sessionId")
userId <- resolver.sessionId2userId(sessionId)
user <- resolveUser(userId)
token <- request.cookies.get(cookieName).map(_.value)
userId <- idContainer.get(token)
user <- resolveUser(userId)
} yield {
resolver.prolongTimeout(sessionId, sessionTimeoutInSeconds)
idContainer.prolongTimeout(token, sessionTimeoutInSeconds)
user
}

Expand Down
22 changes: 16 additions & 6 deletions module/src/main/scala/jp/t2v/lab/play20/auth/AuthConfig.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package jp.t2v.lab.play20.auth

import play.api.mvc.{RequestHeader, PlainResult}
import play.api.mvc._

trait AuthConfig {

Expand All @@ -16,16 +16,26 @@ trait AuthConfig {

def resolveUser(id: Id): Option[User]

def loginSucceeded(request: RequestHeader): PlainResult
def loginSucceeded(request: RequestHeader): Result

def logoutSucceeded(request: RequestHeader): PlainResult
def logoutSucceeded(request: RequestHeader): Result

def authenticationFailed(request: RequestHeader): PlainResult
def authenticationFailed(request: RequestHeader): Result

def authorizationFailed(request: RequestHeader): PlainResult
def authorizationFailed(request: RequestHeader): Result

def authorize(user: User, authority: Authority): Boolean

def resolver(implicit request: RequestHeader): RelationResolver[Id] = new CacheRelationResolver[Id]
def idContainer: IdContainer[Id] = new CacheIdContainer[Id]

lazy val cookieName: String = "PLAY2AUTH_SESS_ID"

lazy val cookieSecureOption: Boolean = false

lazy val cookieHttpOnlyOption: Boolean = true

lazy val cookieDomainOption: Option[String] = None

lazy val cookiePathOption: String = "/"

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package jp.t2v.lab.play20.auth

import play.api.cache.Cache
import play.api.Play._
import scala.annotation.tailrec
import scala.util.Random
import java.security.SecureRandom

class CacheIdContainer[Id: ClassManifest] extends IdContainer[Id] {

private[auth] val tokenSuffix = ":token"
private[auth] val userIdSuffix = ":userId"
private[auth] val random = new Random(new SecureRandom())

def startNewSession(userId: Id, timeoutInSeconds: Int): AuthenticityToken = {
removeByUserId(userId)
val token = generate
store(token, userId, timeoutInSeconds)
token
}

@tailrec
private[auth] final def generate: AuthenticityToken = {
val table = "abcdefghijklmnopqrstuvwxyz1234567890_.!~*'()"
val token = Stream.continually(random.nextInt(table.size)).map(table).take(64).mkString
if (get(token).isDefined) generate else token
}

private[auth] def removeByUserId(userId: Id) {
Cache.getAs[String](userId.toString + userIdSuffix) foreach unsetToken
unsetUserId(userId)
}

def remove(token: AuthenticityToken) {
get(token) foreach unsetUserId
unsetToken(token)
}

private[auth] def unsetToken(token: AuthenticityToken) {
Cache.set(token + tokenSuffix, None, 1)
}
private[auth] def unsetUserId(userId: Id) {
Cache.set(userId.toString + userIdSuffix, None, 1)
}

def get(token: AuthenticityToken) = Cache.get(token + tokenSuffix).map(_.asInstanceOf[Id])

private[auth] def store(token: AuthenticityToken, userId: Id, timeoutInSeconds: Int) {
Cache.set(token + tokenSuffix, userId, timeoutInSeconds)
Cache.set(userId.toString + userIdSuffix, token, timeoutInSeconds)
}

def prolongTimeout(token: AuthenticityToken, timeoutInSeconds: Int) {
get(token).foreach(store(token, _, timeoutInSeconds))
}

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package jp.t2v.lab.play20.auth

import scala.util.control.Exception._

class CookieIdContainer[Id: ToString: FromString] extends IdContainer[Id] {

def startNewSession(userId: Id, timeoutInSeconds: Int) = implicitly[ToString[Id]].apply(userId)

def remove(token: AuthenticityToken) {
}

def get(token: AuthenticityToken) = implicitly[FromString[Id]].apply(token)

def prolongTimeout(token: AuthenticityToken, timeoutInSeconds: Int) {
// Cookie Id Container does not support timeout.
}

}

trait ToString[A] {
def apply(id: A): String
}
object ToString {
def apply[A](f: A => String) = new ToString[A] {
def apply(id: A) = f(id)
}
implicit val string = ToString[String](identity)
implicit val int = ToString[Int](_.toString)
implicit val long = ToString[Long](_.toString)
}
trait FromString[A] {
def apply(id: String): Option[A]
}
object FromString {
def apply[A](f: String => A) = new FromString[A] {
def apply(id: String) = allCatch opt f(id)
}
implicit val string = FromString[String](identity)
implicit val int = FromString[Int](_.toInt)
implicit val long = FromString[Long](_.toLong)
}

This file was deleted.

13 changes: 13 additions & 0 deletions module/src/main/scala/jp/t2v/lab/play20/auth/IdContainer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package jp.t2v.lab.play20.auth


trait IdContainer[Id] {

def startNewSession(userId: Id, timeoutInSeconds: Int): AuthenticityToken

def remove(token: AuthenticityToken): Unit
def get(token: AuthenticityToken): Option[Id]

def prolongTimeout(token: AuthenticityToken, timeoutInSeconds: Int): Unit

}
40 changes: 23 additions & 17 deletions module/src/main/scala/jp/t2v/lab/play20/auth/LoginLogout.scala
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
package jp.t2v.lab.play20.auth

import play.api.mvc.{Request, PlainResult, Controller}
import play.api.mvc._
import play.api.mvc.AsyncResult
import play.api.mvc.Cookie
import scala.annotation.tailrec
import scala.util.Random
import java.security.SecureRandom

trait LoginLogout {
self: Controller with AuthConfig =>

def gotoLoginSucceeded[A](userId: Id)(implicit request: Request[A]): PlainResult = {
resolver.removeByUserId(userId)
val sessionId = generateSessionId(request)
val session = resolver.store(sessionId, userId, sessionTimeoutInSeconds)
loginSucceeded(request).withSession(session + ("sessionId" -> sessionId))
def gotoLoginSucceeded(userId: Id)(implicit request: RequestHeader): Result = {
val token = idContainer.startNewSession(userId, sessionTimeoutInSeconds)
setCookie(loginSucceeded(request), token)
}

def gotoLogoutSucceeded[A](implicit request: Request[A]): PlainResult = {
request.session.get("sessionId") foreach resolver.removeBySessionId
logoutSucceeded(request).withNewSession
def gotoLogoutSucceeded(implicit request: RequestHeader): Result = {
request.cookies.get(cookieName) map (_.value) foreach idContainer.remove
unsetCookie(logoutSucceeded(request))
}

@tailrec
private def generateSessionId[A](implicit request: Request[A]): String = {
val table = "abcdefghijklmnopqrstuvwxyz1234567890_.!~*'()"
val token = Stream.continually(random.nextInt(table.size)).map(table).take(64).mkString
if (resolver.exists(token)) generateSessionId(request) else token
protected[auth] final def setCookie(result: Result, token: AuthenticityToken): Result = {
setCookie_(result, Cookie(cookieName, token, -1, cookiePathOption, cookieDomainOption, cookieSecureOption, cookieHttpOnlyOption))
}

protected[auth] final def unsetCookie(result: Result): Result = {
setCookie_(result, Cookie(cookieName, "", 0))
}

protected[auth] final def setCookie_(result: Result, cookie: Cookie): Result = {
def set(r: Result): Result = r match {
case p: PlainResult => p.withCookies(cookie)
case a: AsyncResult => AsyncResult(a.result.map(set))
}
set(result)
}

private val random = new Random(new SecureRandom())

}

This file was deleted.

Loading

0 comments on commit 1dc399b

Please sign in to comment.