Thursday, October 28, 2010

Roll your own Ajax-Based Captcha in Grails

Recently, I was asked to come up with a better solution to our captcha needs. We have been using ReCaptcha, which is great but difficult to read at times, and has caused frustrated customers and lost sales. I found a great solution at http://www.jkdesign.org/captcha which displays a number of graphics and let's the user choose the right one to prove they are human. Here is a screenshot of my implementation:
To make this work within Grails, I had to make several tweaks. The following files are required:

Create a new controller called Captcha. This can really be named anything, but if you do rename it, it will have to be updated in the jquery.simpleCaptcha-0.2.js file or passed in as an option via the javascript.

package com.berry
import com.berry.BCrypt
import grails.converters.JSON
class CaptchaController {
def index = {
// Generate the SALT to be used for encryption and place in session
def captchaSalt = session.captchaSalt ?: BCrypt.gensalt()
session.selectedCaptchaText = null
session.captchaSalt = captchaSalt
// Modify below for custom images
def images = [
'house': 'images/captchaImages/01.png',
'key': 'images/captchaImages/04.png',
'flag': 'images/captchaImages/06.png',
'clock': 'images/captchaImages/15.png',
'bug': 'images/captchaImages/16.png',
'pen': 'images/captchaImages/19.png',
'light bulb': 'images/captchaImages/21.png',
'musical note': 'images/captchaImages/40.png',
'heart': 'images/captchaImages/43.png',
'world': 'images/captchaImages/99.png'
]
// Create the image array to be returned in JSON format
def size = images.size()
def num = Math.min(params.numImages ? params.int('numImages') : 5, size)
def keys = images.keySet().toList()
def used = []
def random = new Random()
(1..num).each { i ->
def idx = random.nextInt(keys.size())
def item = keys.get(idx)
keys.remove(idx)
used << item
}
// Select the 'chosen' text to be used for authentication and place in session
def selectedText = used[random.nextInt(used.size())]
def hashedSelectedText = BCrypt.hashpw(selectedText, captchaSalt);
session.selectedCaptchaText = hashedSelectedText
// println "SELECTED: ${hashedSelectedText}"
// println "USED: ${used.inspect()}"
// Generate object to be returned
def ret = [
text: selectedText,
images: []
]
used.each { u ->
ret['images'] << [hash: BCrypt.hashpw(u, captchaSalt), file: images[u]]
}
render ret as JSON
}
}

What this controller does is return a JSON object with the data needed to generate the captcha. The JSON appears like so:

{
"text":"heart",
"images":[
{
"hash":"$2a$10$GTcG7U1rt7XFBi4JVImT2Oo.E3D8FCzha2772XuXm7v28Kx2LNL5S",
"file":"images/captchaImages/99.png"
},
{
"hash":"$2a$10$GTcG7U1rt7XFBi4JVImT2Oa5Y/I/cXOUj30kffPqyX0qxTnAACX6O",
"file":"images/captchaImages/43.png"
},
{
"hash":"$2a$10$GTcG7U1rt7XFBi4JVImT2O8zeOa4.ed1s8pZS9AgkalcSSQm9pmbi",
"file":"images/captchaImages/15.png"
},
{
"hash":"$2a$10$GTcG7U1rt7XFBi4JVImT2OSNYwC4RPwhNpuPYBbeNB0j4ozoItwDK",
"file":"images/captchaImages/06.png"
},
{
"hash":"$2a$10$GTcG7U1rt7XFBi4JVImT2OLv6DzHHDX0aB2AwS1YEVZMp9cEpo2sq",
"file":"images/captchaImages/01.png"
}
]
}
view raw response.json hosted with ❤ by GitHub

Now we just need to implement this in our GSP file and controller. Suppose we have a page like shown above with a pickup code and the last 4 digits of the persons phone number. With adding our captcha div and required javascript, our GSP would look like this:

<!-- PLACE IN HEADER -->
<script type="text/javascript" src="${resource(dir:'js',file:'jquery.simpleCaptcha-0.2.js')}"></script>
<style type="text/css">
img.simpleCaptcha {
margin: 2px !important;
cursor: pointer;
}
img.simpleCaptchaSelected {
margin-bottom: 0px;
border-bottom: 2px solid red;
}
</style>
<!-- BODY CONTENTS -->
<g:form action="pickup">
<div class="stylized myform" style="width:542px;">
<h2>Your pickup code will be given to you by your loan consultant</h2>
<g:if test="${flash.message}">
<div class="error">
${flash.message}
</div>
</g:if>
<div class="clearfix formField">
<label class="label_only">Pickup Code</label>
<g:textField name="pickupCode" value="${pickupCode}" autocomplete="no" class="text" />
</div>
<div class="clearfix formField">
<label class="label_only">Last 4 Digits of Phone</label>
<span class="after_checkbox" style="padding-right: 0px;">(XXX) XXX-</span>
<g:textField name="lastFourDigits" value="${lastFourDigits}" autocomplete="no" class="text" maxLength="4" />
</div>
<div class="clearfix formField">
<label class="label_only">Are You Human?</label>
<div style="float: left; margin-left: 10px;">
<!-- Begin Captcha -->
<div id="captcha"></div>
<!-- End Captcha -->
</div>
</div>
<div class="clearfix" style="margin-top: 15px;">
<label class="label_only">&nbsp;</label>
<g:submitButton name="submit" value="Show Me My Offer" class="button" />
</div>
</div>
</g:form>
<script type="text/javascript">
$(document).ready(function() {
$('#captcha').simpleCaptcha({
numImages: 5,
introText: '<p>Are you human? Then pick out the <strong class="captchaText"></strong></p>'
});
});
</script>
view raw form.html hosted with ❤ by GitHub

Finally, we need to perform the validation on the controller side. The modified authentication action would look like the following:

def pickup = {
// Determine if the captcha is picked correctly
if (params.captchaSelection != session.selectedCaptchaText) {
// They selected the correct Captcha image. Continue with Authentication
} else {
flash.message = "You did not click the correct image below. Please Try Again."
}
}

So there ya go. It's actually pretty easy and customers seem to like choosing an image much more than typing a word that is difficult to read.

Tuesday, October 12, 2010

Uploading Files in Grails

Here's the slides from a presentation I did at the UGGUG in May 2010. I found that I needed to use it again today so I posted it.

Here is the source code for the uploader service I created:

package berry
import org.springframework.web.multipart.MultipartFile
import org.codehaus.groovy.grails.web.context.ServletContextHolder
class FileUploadService {
boolean transactional = true
def String uploadFile(MultipartFile file, String name, String destinationDirectory) {
def servletContext = ServletContextHolder.servletContext
def storagePath = servletContext.getRealPath(destinationDirectory)
// Create storage path directory if it does not exist
def storagePathDirectory = new File(storagePath)
if (!storagePathDirectory.exists()) {
print "CREATING DIRECTORY ${storagePath}: "
if (storagePathDirectory.mkdirs()) {
println "SUCCESS"
} else {
println "FAILED"
}
}
// Store file
if (!file.isEmpty()) {
file.transferTo(new File("${storagePath}/${name}"))
println "Saved file: ${storagePath}/${name}"
return "${storagePath}/${name}"
} else {
println "File ${file.inspect()} was empty!"
return null
}
}
}

A tip that you may enjoy is the way I implemented this uploader into a real world application. I wanted to be able to upload files into a folder within the web-app folder for development, but in production, I wanted to put it in a folder that is served via Tomcat directly. So on our production server, I created a folder at /opt/assets and created a symbolic link in the $TOMCAT_ROOT/webapps/assets to point to it. I modified the service as follows:

package berry
import org.springframework.web.multipart.MultipartFile
import org.codehaus.groovy.grails.web.context.ServletContextHolder
import grails.util.GrailsUtil
class FileUploadService {
boolean transactional = true
def String uploadFile(MultipartFile file, String name) {
String storagePath = ""
if (GrailsUtil.environment == "production") {
storagePath = "/opt/assets"
} else {
def servletContext = ServletContextHolder.servletContext
storagePath = servletContext.getRealPath('assets')
}
// Create storage path directory if it does not exist
def storagePathDirectory = new File(storagePath)
if (!storagePathDirectory.exists()) {
print "CREATING DIRECTORY ${storagePath}: "
if (storagePathDirectory.mkdirs()) {
println "SUCCESS"
} else {
println "FAILED"
}
}
// Store file
if (!file.isEmpty()) {
file.transferTo(new File("${storagePath}/${name}"))
println "Saved file: ${storagePath}/${name}"
return "${storagePath}/${name}"
} else {
println "File ${file.inspect()} was empty!"
return null
}
}
}

I then create a folder in my web-app folder named 'assets', being sure to add it to my ignore list for the repository. So once I upload the file, it will save to the correct location.

Now to view the image, all I have to do is the following:

<img src="${resource(dir:'assets', file: 'myfile.jpg')}" />
view raw img.html hosted with ❤ by GitHub

This now will work in both DEVELOPMENT and PRODUCTION enviroments.

http://github.com/cavneb/FileUploader

Friday, October 8, 2010

Passing Data From View to Layout via pageProperty

I am writing a new Grails application which uses the website template Admintasia. Part of the layout gsp file has a section for the header and sub-header. For this to be used, I needed to be able to pass those two strings from the view to the layout.

Here's what my view looks like:

<html>
<head>
<meta name="layout" content="admintasia"/>
<meta name="pageHeader" content="This is a test" />
<meta name="pageSubHeader" content="blah blah blah" />
</head>
<body>
...
view raw gsp_view.html hosted with ❤ by GitHub

And here's what my layout looks like:

<h2><g:pageProperty name="meta.pageHeader" default="MISSING PAGE HEADER" /></h2>
<h4><g:pageProperty name="meta.pageSubHeader" default="" /></h4>
view raw layout.html hosted with ❤ by GitHub

I hope this helps out!

Thursday, October 7, 2010

Using Tag Lib within Controllers and Services

So let's say you want to utilize the standard grails tag lib of formatNumber within your domain class, controller or service. How would you go about doing it? With the help of a blog post by Lucas Teixeira, I was able to get it working without an issue. Here is the code:

class Book {
def grailsApplication
String title
Float cost
static constraints = {
title(blank: false)
cost(blank: false)
}
def String toDisplayString() {
def g = grailsApplication.mainContext.getBean('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
return "${title} costs ${g.formatNumber([number: cost, type: "currency", currencyCode: "USD"])}"
}
}

It's also simple to utilize your own tag libs as well. Here's a tag lib that I created to help with XML formatting:

class CommonTagLib {
static namespace = 'berry'
def formatXML = { attrs, body ->
String rawXML = body().toString()
try {
def slurpedXML = new XmlSlurper().parseText(rawXML)
def writer = new StringWriter()
new XmlNodePrinter(new PrintWriter(writer)).print(slurpedXML)
def formattedResponse = writer.toString()
out << formattedResponse
} catch(Exception e) {
log.info "Unable to parse XML: ${e.message}"
out << rawXML
}
}
}

So to call this tag lib in the domain class, controller or service, I just need to do the following:

def berry = grailsApplication.mainContext.getBean('CommonTagLib')
return berry.formatXML("....")

Just make sure that you include the 'def grailsApplication' in the beginning.

Wednesday, October 6, 2010

Using Criteria Builder with Projections

Something I had to dig around for today was how to perform a sum on a table using Criteria Builder. It seems that it is treated a bit different than a normal query would be. Here is an example of what I tried and failed at:

def c = Transaction.createCriteria()
def cnt = c.get {
projections {
sum("amount")
}
and {
eq("status", k)
eq("transactionType", transactionType)
ge("dateTimeProcessed", cal1.getTime())
le("dateTimeProcessed", cal2.getTime())
}
}
After playing around with it a bit, I found that I can't use the 'and' closure. I revised the code to the following and it worked:
def c = Transaction.createCriteria()
def cnt = c.get {
projections {
sum("amount")
}
eq("status", k)
eq("transactionType", transactionType)
ge("dateTimeProcessed", cal1.getTime())
le("dateTimeProcessed", cal2.getTime())
}