Play! 2.0 Anorm standalone

February 7th, 2012 No comments

Anorm’s latest 2.0 Snapshots can be used standalone.

You have to add the obvious dependency to your code below.

libraryDependencies ++= Seq(
  "mysql" % "mysql-connector-java" % "5.1.10",
  "play" %% "anorm" % "2.0-RC1-SNAPSHOT"
)

Along with the wonderful resolver since Play! 2.0 is not publishing with standard a ivy/maven structure.

resolvers += Resolver.url("Typesafe Ivy Releases", new java.net.URL("http://repo.typesafe.com/typesafe/ivy-snapshots/"))(Patterns("[organisation]/[module]/[revision]/ivy-[revision].xml"::Nil,
    "[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"::Nil, 
    false) )

Also for standalone you will need to create an Implicit Connection to use with your models, especially if you want it to look similar to the normal Play! examples. (This is pretty close rip to the original.)

trait db {

  // Quick way to load the driver
  Class.forName("com.mysql.jdbc.Driver").newInstance
 
  def getConnectionByUrl(url: String) = {
    DriverManager.getConnection(url)
  }
  
  def getConnection(name: String) = {
    getConnectionByUrl("jdbc:mysql://localhost:3306/db_name?user=username&password=pass")
  }

  /*
  def withConnection[A](block: Connection => A): A = {
    withConnection("default")
  } */

  def withConnection[A](name: String)(block: Connection => A): A = {
    val connection = getConnection(name) 
    try {
      block(connection)
    } finally {
      connection.close()
    }
  }

  def withTransaction[A](name: String)(block: Connection => A): A = {
    withConnection(name) { connection =>
      try {
        connection.setAutoCommit(false)
        val r = block(connection)
        connection.commit()
        r
      } catch {
        case e => connection.rollback(); throw e
      }
    }
  }
}
object db extends db {
  def withConnection[A](block: Connection => A): A = {
    this.withConnection("default")(block)
  }

  def withTransaction[A](block: Connection => A): A = {
    this.withTransaction("default")(block)
  }
}

Now you can create a very basic model such as:

case class Obj (
                    id: Pk[Long],
                    name: String
                    ) {}

object Obj {

  val simple = {
    get[Pk[Long]]("objs.id") ~
    get[String]("objs.name")  map {
      case id ~ name  => Obj(
        id, name
      )
    }
  }

  def findById(id: Long) = {
    db.withConnection {
      implicit connection =>
        SQL("select * from objs where id = {id};").on(
          'id -> id
        ).as(Obj.simple.singleOpt)
    }
  }
}
Categories: Scala Tags: ,

Scalatra, Form authentication with Remember Me

August 24th, 2011 1 comment

After my last Basic Authentication example with Scalatra I felt we had a need to get a better example of authentication available for most users to review or use as a basis for their own implementation. The strategies listed below has a User plus Password authentication and a Remember Me cookie authentication availability. Both of these strategies can and when appropriate should be extended to include more security. Additionally for API integration you could use either of these examples to authenticate a user based on a header parameter as an example.

Read more…

Categories: Scala, Scalatra Tags:

Scalatra, an example authentication app

August 7th, 2011 No comments

I wanted to post up a quick example app after spending a lot of time working through issues and questions on my own.

First thing first. We are going to use the quick start for a new project. The quickest method is to use n8han/giter8 ( https://github.com/n8han/giter8 ). We will also use MongoDB with Lift’s Mongo-Record for a quick user model.

One installed we are going to create our basic project.

$ g8 scalatra/scalatra-sbt
> organization [com.example]: 
> name [scalatra-sbt-prototype]: scalatra-auth 
> servlet_name [MyScalatraFilter]: 
> scala_version [2.9.0-1]: 
> version [1.0]: 
$ cd scalatra-auth

If you do not have SBT installed at this point follow setup docs (https://github.com/harrah/xsbt/wiki/Setup).

In `Build.sbt` add the library dependencies below.

"net.liftweb" %% "lift-json" % "2.4-M3",
"net.liftweb" %% "lift-mongodb-record" % "2.4-M3"

Create a new file `/src/main/scala/AuthenticationSupport.scala` this file is used to tell the Scalate servlet how to support authenticating our used model we are about to create.

import org.scalatra.auth.{ScentryConfig, ScentrySupport}
import org.scalatra.ScalatraKernel
import org.scalatra.auth.strategy.{BasicAuthStrategy, BasicAuthSupport}
import org.scalatra.auth.{ScentrySupport, ScentryConfig}


trait AuthenticationSupport extends ScentrySupport[User] with BasicAuthSupport[User] { self: ScalatraKernel =>

    val realm = "test"
    protected def contextPath = request.getContextPath

    protected def fromSession = { case id: String => User.find("_id",id) 
                                   openOr null }
    protected def toSession   = { case usr: User => usr.id.toString() }

    protected val scentryConfig = (new 
          ScentryConfig{}).asInstanceOf[ScentryConfiguration]


    override protected def configureScentry = {
      scentry.unauthenticated {
        scentry.strategies('Basic).unauthenticated()
      }
    }

    override protected def registerAuthStrategies = {
      scentry.registerStrategy('Basic, app =>
                new OurBasicAuthStrategy(app, realm))
    }
  }

The registered strategy in the example above needs created now place that in `src/main/scala/OurBasicAuthStrategy.scala`.

import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
import net.iharder.Base64
import org.scalatra.{ScalatraKernel}
import net.liftweb.mongodb._
import net.liftweb.mongodb.record._
import org.scalatra.auth.strategy.{BasicAuthStrategy, BasicAuthSupport}
import org.scalatra.auth.{ScentrySupport, ScentryConfig}

class OurBasicAuthStrategy(protected override val app: ScalatraKernel,
     realm: String)
  extends BasicAuthStrategy[User](app, realm) {

  protected def validate(userName: String, password: String): Option[User] = {
    return User.login(userName, password)
  }

  protected def getUserId(user: User): String = user.userIdAsString
}

Now we will need all of our user functions to register, login, and validate the session. Create the user model in `src/main/scala/User.scala`.

import java.util.regex.Pattern
import net.liftweb.mongodb.record._
import net.liftweb.mongodb.record.field._
import net.liftweb.record.field._
import net.liftweb.record._
import org.bson.types._
import org.joda.time.{DateTime, DateTimeZone}
import net.liftweb.mongodb.record.MongoRecord
import net.liftweb.json.DefaultFormats
import net.liftweb.json.JsonDSL._
import net.liftweb.json.JsonAST.JObject


class User extends MongoRecord[User] with MongoId[User] {
  def meta = User

  object username extends StringField(this, 200)
  object password extends StringField(this, 200)

  def userIdAsString: String = id.toString

  def login(u: String, p: String): Option[User] = {
    val user = User.findAll(("username" -> u), ("password" -> p))
	Some(user.first)
  }
}
object User extends User with MongoMetaRecord[User] {
}

Now that we have our Authentication support and our User model we need to create the actual handing within our example application.

import org.scalatra._
import java.net.URL
import scalate.ScalateSupport

import net.liftweb.mongodb._
import net.liftweb.json._
import net.liftweb.mongodb.record.MongoRecord



class MyScalatraFilter extends ScalatraFilter with ScalateSupport with AuthenticationSupport{
  
	beforeAll {
		MongoDB.defineDb(DefaultMongoIdentifier, MongoAddress(MongoHost("127.0.0.1", 27017), "scalatra-auth"))
	}

  get("/") {
    
      
        

Hello, world!

Say hello to Scalate. Test Auth Register. } get("/login") { contentType = "text/html"

Login

} post("/login") { basicAuth redirect("/loggedin") } get("/loggedin") { contentType = "text/html" basicAuth

Hello, world!

Welcome {user.username} you are logged in. } get("/register") { contentType = "text/html"

Register

} afterAll { MongoDB.close } post("/register") { val u = User.createRecord .username(params("userName")) .password(params("password")) u.save redirect("/login") } notFound { // If no route matches, then try to render a Scaml template val templateBase = requestPath match { case s if s.endsWith("/") => s + "index" case s => s } val templatePath = "/WEB-INF/scalate/templates/" + templateBase + ".scaml" servletContext.getResource(templatePath) match { case url: URL => contentType = "text/html" templateEngine.layout(templatePath) case _ => filterChain.doFilter(request, response) } } }

Now we should verify all the hard work we put into this test application pays off. (Make sure you have Mongod running)

$ sbt jetty-run

You will be able to walk through a basic registration and authentication process now.

To clone or review this code please visit github (https://github.com/jlarmstrong/scalatra-auth-example).

Categories: Scala Tags: , ,

Lighty LUA fun

March 24th, 2009 No comments

I have been fighting for a while now with making LightTPD play nice with me. I finally have a pretty good recipe coming together. This is far from complete but works pretty well for me at this point. This is my CakePHP recipe which so far has been running like a charm. This combines a CakePHP LUA rewrite with a Maintenance LUA script. I haven’t had a chance to convert the CSS/JS rewrite yet as I plan on adding compression handling when I finally do that. The best part about this maintenance handler is that unlike other Lighty recipies I have found this one checks for existence if the remote ip is a normal user/admin and for the maintenance file to actually exist. Instead of taking down all vhosts you can work with upgrading/maintaining 1 at a time.

VHOST.conf

$HTTP["host"] =~ "test\.site\.com" {
server.document-root = "/var/www/test_site_com/httpdocs"

url.rewrite = (
"(css|files|img|js)/(.*)" => "/$1/$2"
)

magnet.attract-physical-path-to += ( "/etc/lighttpd/cakephp.lua" )

$HTTP["remote-ip"] != "127.0.0.1" {
magnet.attract-physical-path-to += ( "/etc/lighttpd/maint.lua" )
}

}

cakephp.lua

-- little helper function
function file_exists(path)
local attr = lighty.stat(path)
if (attr)  then
return true
else
return false
end
end
function removePrefix(str, prefix)
return str:sub(1,#prefix+1) == prefix.."/" and str:sub(#prefix+2)
end
-- prefix without the trailing slash
local prefix = ''
-- the magic 

if (not file_exists(lighty.env["physical.path"])) then
-- file still missing. pass it to the fastcgi backend
request_uri = removePrefix(lighty.env["uri.path"], prefix)
if request_uri then
lighty.env["uri.path"]                  = prefix .. "/index.php"
local uriquery                                  = lighty.env["uri.query"] or ""
lighty.env["uri.query"]                 = uriquery .. (uriquery ~= "" and "&" or "") .. "url=" .. request_uri
lighty.env["physical.rel-path"] = lighty.env["uri.path"]
lighty.env["request.orig-uri"]  = lighty.env["request.uri"]
lighty.env["physical.path"]             = lighty.env["physical.doc-root"] .. lighty.env["physical.rel-path"]
end
end
-- fallthrough will put it back into the lighty request loop
-- that means we get the 304 handling for free. ;) 

maint.lua

-- little helper function
function file_exists(path)
local attr = lighty.stat(path)
if (attr)  then
return true
else
return false
end
end
function removePrefix(str, prefix)
return str:sub(1,#prefix+1) == prefix.."/" and str:sub(#prefix+2)
end
-- prefix without the trailing slash
local prefix = ''
-- the magic

if ( file_exists(lighty.env["physical.doc-root"] .. "/maintenance.html")) then
lighty.header["X-Maintenance-Mode"] = "1"
lighty.content = { { filename = lighty.env["physical.doc-root"] .. "/maintenance.html" } }
lighty.env["uri.path"]                  = prefix .. "/maintenance.html"
lighty.env["uri.query"]                 = ""
lighty.env["physical.rel-path"] = lighty.env["uri.path"]
lighty.env["physical.path"]             = lighty.env["physical.doc-root"] .. lighty.env["physical.rel-path"]
lighty.header["Content-Type"] = "text/html"
return 503
end
Categories: Uncategorized Tags: , , ,

ParseIt, wow it all still exists

March 18th, 2009 No comments

I found out besides the old files i have managed to keep a hold of that the website and files still exists out there. I honestly would have expected planetquake gamespy to have thrown this all away by now. Perhaps there are still people downloading it?

Last updated site:

http://hosted.planetquake.gamespy.com/parseit/index.shtml

Files:

http://www.gamers.org/pub/idgames2/planetquake/parseit/

Categories: Uncategorized Tags: