Monday, 20 October 2008

Grails: Hacking JSecurity plugin to supports OpenId

I'm currently developing an application for my new brand company and I'd like that supports authentication with username and password , and OpenId.

I could install Acegi Grails Plugin, but I'm very happy using JSecurity, ok no problem let's hack.
First, I have to install OpenId Plugin to support OpenId authenticantion, with this plugin I can manage login process and get openid identifier for OpenId users.

With JSecurity installed and done QuickStart, I need to pass Openid identifier in auth process, for this I've created class OpenIdContextHolder to save in a ThreadLocal context.

class OpenIdContextHolder{

private static final ThreadLocal openIdContextHolder = new ThreadLocal();

static void resetContext() {
openIdContextHolder.set(null);
}

static def getOpenIdIdentifier(){
openIdContextHolder.get()
}

static void setOpenIdIdentifier(id){
openIdContextHolder.set(id)
}

}
Now I have to hack AuthController to manage openid issues. The trick is when a user try to signIn, if I recive an OpenId identifier from OpenId Plugin then I put it in OpenIdContextHolder.

def login = {
if(openidService.isLoggedIn(session)){
return redirect(action:'signIn')
}

return [ username: params.username, rememberMe: (params.rememberMe != null), targetUri: params.targetUri ]
}

def signIn = {
// if is logged with openid set contextholder
if(openidService.isLoggedIn(session)){
def openId = openidService.getIdentifier(session)
OpenIdContextHolder.setOpenIdIdentifier(openId)
params.rememberMe = true
params.username = openId
params.password = "nullpass"
}
def authToken = new UsernamePasswordToken(params.username, params.password)

// continues the default generated code...
// ...
}
Next step is update JsecDbRealm generated. I have to retrieve OpenId identifier from ContextHolder, and lookup in my domain objects. I use the trick to register my openId users in JScecurity with the password 'secret' but if anyone try to access with username and password in this kind of users, I throw an Exception.
def authenticate(authToken) {
log.info "Attempting to authenticate ${authToken.username} in DB realm..."

// experimental!!
def openid = OpenIdContextHolder.getOpenIdIdentifier()
OpenIdContextHolder.resetContext()
log.info "OpenIdContextHolder request with openid: ${openid}"
if(openid){
def openidUser = User.findByOpenid(openid)
if (!openidUser)
throw new UnknownAccountException("No account found for user [${username}]")
log.info "Jsecurity with Openid ${openidUser.username} : ${openidUser.openid}"
authToken.password = 'secret'
authToken.username = openidUser.username
}else {
def openidUser = User.findByUsername(authToken.username)
if(openidUser?.openid?.trim()){
// trying to access with password for openid user
log.info "Jsecurity: Trying to access with password for user: ${openidUser.username} : ${openidUser.openid}"
throw new IncorrectCredentialsException("Invalid password for openid user '${authToken.username}', try to use openid instead user:password")
}
}

def username = authToken.username
// continues the default generated code...
// ...
The last step is redirect, openid users to signIn controller after logged (you only have to change it in OpenId Plugin).

And that's all folks.

Saturday, 6 September 2008

New hopes and expectations

I've been very busy those months, because I have a lot of work to do. The main reason is that I quit my old job and I've started the adventure to work in my own company.

Now I work for the best company in the world Heralsoft, and of course the best company only can use the best technology, Grails :D.
To start, I eat my own food because the company website is a Grails application. In less than one week of development, I did a professional site with i18n (English and Spanish), blog (only English), feeds, authentication, searchable.

Visit my company home page and contact to me if you have any comment, or business oportunities.

Wednesday, 18 June 2008

Script to deploy Grails Applications Remote

I spent some time this week doing a script to deploy my grails applications in remote Jetty container.
To do my deploy script I use Gant, with Grails support. To deploy War in Jetty you have two options:

  • Static. To do in static way you only have to copy your War to "webapps" directory and restart container.
  • Hot deploy. I prefer hot deploy to avoid restart container and I have to copy War in "webapps" directory and generate XML with context description.
To copy War remote, Ant.scp is the best way, make sure that you have Ant optional task and and jsch.jar in classpath (you can copy from Eclipse to GRAILS_HOME/lib for example).

To generate XML, Groovy is fantastic I mix some withWriter closure to generate the head of the document, and MarkupBuilder to do the hard work.

And this is the script:
import org.codehaus.groovy.grails.commons.GrailsClassUtils as GCU
import groovy.xml.MarkupBuilder

grailsHome = Ant.project.properties."environment.GRAILS_HOME"

includeTargets << new File ( "${grailsHome}/scripts/War.groovy" )

target('default': "Deploy war in server") {
depends(war)

// avoid War with dots (problems with static deploy)
def warTmp = "${grailsAppName}.war"
Ant.copy(file:warName, tofile:warTmp)

def dir = "/opt/jetty"

def host = "myhost.com"
def port = "22"
def user = "myuser"
def dsa = "${basedir}/scripts/key/id_dsa"


def swriter = new StringWriter()
def xml = new MarkupBuilder(swriter)

xml.Configure('class': "org.mortbay.jetty.webapp.WebAppContext") {
Set(name: 'contextPath','/')
Set(name: 'war'){
SystemProperty(name: 'jetty.home', default: '.')
mkp.yield("/webapps/${warTmp}")
}
Set(name: 'defaultsDescriptor'){
SystemProperty(name: 'jetty.home', default: '.')
mkp.yield("/etc/webdefault.xml")
}

}
def xmlDescriptor = "${basedir}/${grailsAppName}.xml"
new File(xmlDescriptor).withWriter{ writer ->
writer << '<?xml version="1.0" encoding="ISO-8859-1"?>\r\n'
writer << '<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">\r\n'
writer << swriter.toString()
writer << '\r\n'
}

echo "Deploying files to remote: ${host}"
scp(file: warTmp, todir: "${user}@${host}:${dir}/webapps", keyfile:"${dsa}", port:"${port}", passphrase:"", trust:"true")
scp(file: xmlDescriptor, todir: "${user}@${host}:${dir}/contexts", keyfile:"${dsa}", port:"${port}", passphrase:"", trust:"true")
echo "Finished :)"
}

I have an issue with hot deploy in Jetty and Grails (if anyone can help?) , but the script works well :D

Saturday, 17 May 2008

The advantage of Grails

This week Spring Source have released, Spring Web Flow 2, I've read release notes and this make me think about the advantage of Grails.

Let's see release notes:

DSL to define flows the fact is XML+EL. I think is much readable Grails DSL to define WebFlows and less verbose.
  • Spring Javascript is a JavaScript abstraction framework that allows you to progressively enhance a web page with behavior.
Spring Javascript is built over Dojo to provide Ajax abstractions and rich widget behavior, and Grails do it, with tags, and Ajax agnostic calls (Prototype, Dojo, YUI, etc).
  • The Spring Faces module contains Spring's support for JavaServerFaces.
Who cares JSF? The EJBs in the view layer, jeje.
  • Using Spring Security to secure your flows in a declarative manner
A lot of security plugins for Grails.
  • Using Tiles for JSP page composition and Ajax partial-rendering
Grails layouts, templates and content negotiation are fantastic to do this.

And the best, Grails can be updated to use Spring Webflow 2.

But, the advantage goes further, with Servlet 3.0 and generation of web.xml, I can do it with Grails plugins, and really futher with Java 7 and closures.

Monday, 12 May 2008

Great feedback in Grails Course

In last week I've been teaching in Groovy and Grails course, around 15 students, some people from Struts 1.x, some people from PHP, some portal developers with Vignette, and newbie people from University.

The people were very interested in Grails, and all said that is very easy learn, and create Web apps.

Great experience.

Thursday, 17 April 2008

Grails course in Asturias, Spain

I'm pleased to annonce a Grails course in Asturias, Spain. The course is organized by the College of Engineers of Principado de Asturias, from 5th to 10th of may, around 15 hours training in Groovy and Grails technologies.

The title of course can be translate as "Agile Development in Java EE with Grails". If any one is interested, can contact to me by mail.

Here the slides (in Spanish) for my first class.

Monday, 7 April 2008

Grails: Plugglabe Service, Reloaded

Yesterday I've read, the post from Josh Creed about a pluggable service in Grails and I've been thinking the way to do with IoC and wire provider with Spring.

Let's do it, with small test.

First my service class GreetService:

class GreetService {

def provider

def sayHello(String name){
provider.sayHello(name)
}

}
Now, two Providers:
class DevProvider {
def sayHello(String name){
println "DevProvider: hello ${name}"
}
}


class TestProvider {
def sayHello(String name){
println "TestProvider: hello ${name}"
}
}
Now I'm gonna use Spring DSL, in resources.groovy, to define provider bean that depends of environment:
import grails.util.*
beans = {
switch(GrailsUtil.environment) {
case "test":
provider(TestProvider)
break
case "development":
provider(DevProvider)
break
}
}
And that's all with Groovy and Grails magic, here the test:

class GreetServiceTests extends GroovyTestCase {

def greetService

void testGreet() {

greetService.sayHello("World!!!!!!")
}
}
// output: TestProvider: hello World!!!!!!
And in Grails console, another test:

def service = ctx.getBean("greetService")
service.sayHello("world")

// output: DevProvider: hello world

Thursday, 13 March 2008

Grails: JettyStatic plugin released

The past month I did a plugin to server static content with jetty for grails, now I publish the plugin for anyone that could be useful.

Thursday, 28 February 2008

Grails: Hacking paginate tag to make Ajax way

I one thing I miss in Grails is a remotePaginate Taglib (you can vote in Jira), today I'm going to hack paginate tag to make an Ajax paginate.

For this I have various options, I can modify the actual paginated taglib or the option I've chosen, hack with prototype observe the links generated by paginate tag.

To make easy, I've used observe tag, to capture clicks events, let's go with the example of list.gsp:

You have to wrap your list with div for this example 'ajax_wrap' to refresh with Ajax calls.

<div id="ajax_wrap">

<div class="list">
<g:render template="archive" collection="${archiveList}" var="archive"/>
</div>

<div class="paginateButtons">
<g:paginate action="list" total="${Archive.count()}" />
<g:observe classes="${['step','prevLink','nextLink']}" event="click" function="clickPaginate"/>
</div>

</div>
I've used g:observe to capture click events in generated links by g:paginate, and define a function handler clickPaginate that performs the Ajax calls.

Here function handler defnition:
function clickPaginate(event){
event.stop();
var link = event.element();
if(link.href == null){
return;
}

new Ajax.Updater(
{ success: $('ajax_wrap') },
link.href,
{
evalScripts: true
});
}
The Ajax call update the element 'ajax_wrap'.

With this you have an elegant way to make your list paginated effortlessly

Monday, 18 February 2008

Grails: Tip for scaffolding required fields

Scaffolding is one of the most useful features of grails and customizing scaffolding it's basic for increase your productivity.

One thing I've customized is render a '*' for required fields, for this you have to install scaffolding templates with grails install-templates.

This is a fragment of the original templates/scaffolding/create.gsp,


props.each { p ->
if(p.type != Set.class) {
cp = domainClass.constrainedProperties[p.name]
display = (cp ? cp.display : true)
if(display) { %>
<tr class="prop">
<td valign="top" class="name">
<label for="${p.name}">${p.naturalName}:</label>
</td>
<td valign="top" class="value \${hasErrors(bean:${domainClass.propertyName},field:'${p.name}','errors')}">
${renderEditor(p)}
</td>
</tr>
<% } } } %>


I'm going to introduce a line, to check if the field has a constraint blank:false, and in this case render as a required field.

if(!cp?.blank) { %><span class="req">*</span><% } %>

The updated version complete:
   props.each { p ->
if(p.type != Set.class) {
cp = domainClass.constrainedProperties[p.name]
display = (cp ? cp.display : true)
if(display) { %>
<tr class="prop">
<td valign="top" class="name">
<% if(!cp?.blank) { %><span class="req">*</span><% } %>
<label for="${p.name}">${p.naturalName}:</label>
</td>
<td valign="top" class="value \${hasErrors(bean:${domainClass.propertyName},field:'${p.name}','errors')}">
${renderEditor(p)}
</td>
</tr>
<% } } } %>

That's all folks.