Permalink
Browse files

Initial checkin

  • Loading branch information...
0 parents commit 3628ac9a5fffc5fd7fbf64f4df561156caa7f0a9 @wsargent committed Jan 9, 2012
@@ -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.