Permalink
Browse files

Initial checkin

  • Loading branch information...
wsargent committed Jan 9, 2012
0 parents commit 3628ac9a5fffc5fd7fbf64f4df561156caa7f0a9
@@ -0,0 +1,5 @@
logs
project/project
project/target
target
tmp
@@ -0,0 +1,51 @@
import models.User
import play.api._
import org.apache.shiro.mgt.DefaultSecurityManager
import security.{StringPasswordEncryptor, SampleRealm}
/**
*
* @author wsargent
* @since 1/8/12
*/
object Global extends GlobalSettings {
override def onStart(app: Application) {
ShiroConfig.initialize()
InitialData.insert()
}
}
object ShiroConfig {
def initialize() {
val sampleRealm = new SampleRealm()
val securityManager = new DefaultSecurityManager()
securityManager.setRealm(sampleRealm)
org.apache.shiro.SecurityUtils.setSecurityManager(securityManager)
}
}
/**
* Initial set of data to be imported
* in the sample application.
*/
object InitialData {
def insert() = {
if(User.findAll.isEmpty) {
Seq(
User("admin@example.com", "admin")
).foreach(User.create)
}
}
}
@@ -0,0 +1,33 @@
package controllers
import play.api._
import play.api.mvc._
import play.api.data._
import models.User
object Application extends Controller with Authentication {
def index = Action { implicit request =>
Ok(views.html.index(User.findAll, currentUser))
}
def logout = Action { implicit request =>
User.logout()
Redirect(routes.Application.index).withNewSession.flashing(
"success" -> "You've been logged out"
)
}
}
trait Authentication {
/**
* Retrieve the connected user email.
*/
def authToken(request: RequestHeader) = request.session.get("email")
def currentUser(implicit request: RequestHeader) : Option[User] = authToken(request).flatMap { User.findByEmail(_) }
}
@@ -0,0 +1,49 @@
package controllers
import views.html
import play.api._
import play.api.mvc._
import play.api.data._
import models.User
/**
*
* @author wsargent
* @since 1/8/12
*/
object Login extends Controller {
// -- Authentication
val loginForm = Form(
of(
"email" -> email,
"password" -> text
) verifying ("Invalid email or password", result => result match {
case (email, password) => User.authenticate(email, password)
})
)
/**
* Login page.
*/
def index = Action { implicit request =>
Ok(html.login(loginForm))
}
/**
* Handle login form submission.
*/
def authenticate = Action { implicit request =>
val result = loginForm.bindFromRequest.fold(
formWithErrors => { BadRequest(html.login(formWithErrors)) },
success => { Redirect(routes.Application.index).withSession("email" -> success._1) }
)
result.asInstanceOf[Result]
}
}
@@ -0,0 +1,44 @@
package controllers
import views.html
import models._
import play.api.mvc._
import play.api.data._
/**
*
* @author wsargent
* @since 1/8/12
*/
object Register extends Controller {
val registerForm = Form(
of(
"email" -> text,
"password" -> text
) verifying("Cannot register user", result => result match {
case (email, password) => User.register(email, password)
})
)
def index = Action(implicit request =>
Ok(html.register(registerForm))
)
def register = Action {
implicit request =>
val result = registerForm.bindFromRequest.fold(
formWithErrors => {
BadRequest(html.login(formWithErrors))
},
success => {
Redirect(routes.Application.index).withSession("email" -> success._1)
}
)
result.asInstanceOf[Result]
}
}
@@ -0,0 +1,98 @@
package models
import org.apache.shiro.SecurityUtils
import org.apache.shiro.authc.{AuthenticationException, UsernamePasswordToken}
import play.api.db._
import play.api.Play.current
import anorm._
import anorm.SqlParser._
import security.StringPasswordEncryptor
/**
*
* @author wsargent
* @since 1/8/12
*/
case class User(email: String, password: String)
object User {
val passwordEncryptor = StringPasswordEncryptor
/**
* Parse a User from a ResultSet
*/
val simple = {
get[String]("user.email") ~/
get[String]("user.password") ^^ {
case email~password => User(email, password)
}
}
def findByEmail(email: String): Option[User] = {
DB.withConnection {
implicit connection =>
SQL("select * from user where email = {email}").on(
'email -> email
).as(User.simple ?)
}
}
def findAll: Seq[User] = {
DB.withConnection {
implicit connection =>
SQL("select * from user").as(User.simple *)
}
}
def authenticate(email: String, password: String): Boolean = {
// Use shiro to pass through a username password token.
val token = new UsernamePasswordToken(email, password)
//token.setRememberMe(true)
val currentUser = SecurityUtils.getSubject
try {
currentUser.login(token)
true
} catch {
case e: AuthenticationException => false
}
}
def logout() {
SecurityUtils.getSubject.logout()
}
def register(email: String, password: String): Boolean = {
findByEmail(email) match {
case None => {
create(User(email, password))
true
}
case _ => false
}
}
def create(user: User): User = {
DB.withConnection {
implicit connection =>
SQL(
"""
insert into user values (
{email}, {password}
)
"""
).on(
'email -> user.email,
'password -> passwordEncryptor.encryptPassword(user.password)
).executeUpdate()
user
}
}
}
@@ -0,0 +1,80 @@
package security
import org.apache.shiro.realm.AuthorizingRealm
import org.apache.shiro.authc._
import org.apache.shiro.authc.credential._
import org.apache.shiro.subject._
import org.apache.shiro.authz._
/**
* Custom realm, with thanks to
* <a href="https://github.com/Arnauld/scalaadin/wiki/Authentication:-Vaadin+Shiro">the Vaadin Shiro integration</a>.
*
* @author wsargent
* @since 1/8/12
*/
class SampleRealm extends AuthorizingRealm {
val userService = models.User
val passwordEncryptor = StringPasswordEncryptor
override protected def doGetAuthenticationInfo(token: AuthenticationToken): AuthenticationInfo = {
val upToken = token.asInstanceOf[UsernamePasswordToken]
val username = upToken.getUsername
checkNotNull(username, "Null usernames are not allowed by this realm.")
// retrieve the 'real' user password
val password = passwordOf(username)
checkNotNull(password, "No account found for user [" + username + "]")
// return the 'real' info for username, security manager is then responsible
// for checking the token against the provided info
new SimpleAuthenticationInfo(username, password, getName)
}
override def getCredentialsMatcher = new CredentialsMatcher() {
// Note that the password is salted, and so all password comparisons
// MUST be done through the password encryptor.
def doCredentialsMatch(token: AuthenticationToken, info: AuthenticationInfo) = {
val message = new String(token.getCredentials.asInstanceOf[Array[Char]])
val digest = info.getCredentials.toString
val result = passwordEncryptor.checkPassword(message, digest)
result
}
};
private def passwordOf(username:String) : String = {
userService.findByEmail(username) match {
case Some(user) => user.password
case None => null
}
}
def doGetAuthorizationInfo(principals: PrincipalCollection):AuthorizationInfo = {
checkNotNull(principals, "PrincipalCollection method argument cannot be null.")
import scala.collection.JavaConversions._
val username = principals.getPrimaryPrincipal.asInstanceOf[String]
val info = new SimpleAuthorizationInfo(rolesOf(username))
info.setStringPermissions(permissionsOf(username))
info
}
private def permissionsOf(username:String):Set[String] = Set()
private def rolesOf(username:String):Set[String] = {
username match {
case "admin@example.org" => Set("admin")
case _ => Set.empty
}
}
private def checkNotNull(reference: AnyRef, message: String) {
if (reference == null) {
throw new AuthenticationException(message)
}
}
}
@@ -0,0 +1,25 @@
package security
import org.jasypt.digest._
import org.jasypt.digest.config._
import org.jasypt.util.password.PasswordEncryptor
/**
* A password encryptor that passes through to the string digester on the backend.
*/
object StringPasswordEncryptor extends PasswordEncryptor
{
private def generateStringDigester : StringDigester = {
val d = new StandardStringDigester()
val config = new SimpleDigesterConfig()
d.setConfig(config)
d
}
lazy val stringDigester = generateStringDigester
def checkPassword(message: String, digest: String) = stringDigester.matches(message, digest)
def encryptPassword(p1: String) = stringDigester.digest(p1)
}
Oops, something went wrong.

0 comments on commit 3628ac9

Please sign in to comment.