Tropical Software Observations

18 December 2008

Posted by Teo Choong Ping

at 10:56 AM

3 comments

Labels:

Grails 1.0.4 "no Session" Error

In Grails 1.0.4, there are a few significant changes to how GORM returns Model objects from queries or model traversal.

If you're getting "Could not initialize proxy - no Session" exceptions when trying to access a domain object's collection of child objects, check that you aren't storing the domain object in a HTTP session, as when model objects are stored in the session, they're detached. Prior to version 1.0.3, the detached object had enough information as it was "eagerly fetched". With 1.0.4, this is no longer the case as it's just a proxied object.

To prevent this problem, before you access the object's child collection do the following:

def user = session.user
user.refresh() // Either refresh the object to attach it back to the session
user = User.get( user.id ) // Or retrieve back the object with its id
user.profiles[0] // Then doing stuff like this won't throw the error anymore

For more discussion and context, read this message thread.

02 December 2008

Posted by Irregular Zero

at 11:10 AM

0 comments

Labels:

Clickpass plugin for Grails, Part 2

Go to Clickpass and create an account. Once in the account, go to the "My Details" tab and check the "Show software developer tools" under "Additional Preferences". There should now be a "developer" tab present, select it and push the "Create new site" button.

1. Register site

  • Type in the site name.
  • URL: http://localhost:8080/makedo. This is for running a grails app on your own machine named makedo.
  • Description and logo are optional.

2. Log users in
  • OpenID trust root: http://localhost:8080.
  • begin_openid_login: http://localhost:8080/makedo/openid/login?success_controller=
    clickpass&success_action=welcome&error_controller=&error_action=
    . This link is where Clickpass will access, providing the OpenID details, after one has passed the bar that should be in the app.
  • Leave the OpenID parameter label alone.
  • Submission method: POST.
  • SSL is optional.
  • Once you have saved the changes, the page will change and display a choice of clickpass bars and the html code for it down below. Copy the html code into _clickpass.gsp.

3. Merge accounts
  • begin_add_openid_to_user URL: http://localhost:8080/makedo/clickpass/merge. This is after the authentication and the check whether the OpenID belongs to any user on the database, bringing him to the registration form. If the user selects to login a pre-existing account, the username and password he provides will be sent to this link. If successful the OpenID will be associated with that account.
  • user_id label: Username. This is for the login page Clickpass will provide before accessing the URL above. Merely cosmetic.
  • Adding the connect bar is optional.

4. Register new signups
  • process_openid_registration_url: http://localhost:8080/makedo/clickpass/register. This is after the authentication and the check whether the OpenID belongs to any user on the database, bringing him to the registration form. The user provides information to Clickpass which will be passed along to the link. If successful a new user account with the associated OpenID will be created and saved in your app.
  • new_openid_completion_url: http://localhost:8080/makedo/clickpass/welcome. This is for when authentication is successful and all other things have been taken cared of, e.g. registration or merging.
  • The others are optional except for the fields. Changes in the fields may require changes in the register method of the Clickpass controller.
  • The URL for the registration form should be left for last as it changes due to the fields. Once you're satified and have saved the changes, you can copy and paste the string into urlString in the welcome method of the Clickpass controller.
Aftermath
Once the settings are done, you can use Clickpass.

Clickpass plugin for Grails, Part 1

Clickpass works by handling the OpenID authentication and sending it back to the site. This means that any Grails app using this plugin must also install the OpenID plugin. Also the site will need to be registered with Clickpass and some settings need to be set online. When the authenticated details are sent back, the user is logged in and sent to the welcome page, sent to a form for details to complete registration or asked to login to attach the OpenID to an existing user account.

  • grails create-plugin clickpass.
  • Add ClickpassController.groovy to grails-app/controller.
  • Add User.groovy to grails-app/domain. This is a dummy file required for the plugin to be packaged.
  • Add ClickpassTagLib.groovy to grails-app/taglib.
  • Add _clickpass.gsp to grails-app/views.
  • Add welcome.gsp to grails-app/views/clickpass.

ClickpassController.groovy
class ClickpassController {
def openidService

def index = { response.sendError(404) }

def welcome = {
def user = null

if (openidService.isLoggedIn(session)) {
user = User.findByOpenid(openidService.getIdentifier(session))
if (user == null) {
def urlString = "http://www.clickpass.com/process_new_openid?
site_key=Xr2zelexGn&process_openid_registration_url=
http%3A%2F%2Flocalhost%3A8080%2Fsocialr%2Fclickpass%2F
register&new_openid_completion_url=http%3A%2F%2F
localhost%3A8080%2Fsocialr%2Fclickpass%2Fwelcome&
requested_fields=nickname%2Cemail&required_fields=&
nickname_label=Nickname&email_label=Email"
redirect(url:urlString)
}
}
}

def merge = {
def user = User.findByUsernameAndPassword(params.user_id, params.password)
def urlredirect =
java.net.URLDecoder.decode(params.clickpass_merge_callback_url) +
"&userid_authenticated=false"

if (user == null) {
redirect(url:urlredirect)
}

user.openid = openidService.getIdentifier(session)

if (user.save()) {
redirect(controller:'clickpass',action:'welcome')
}
else {
redirect(url:urlredirect)
}
}

def register = {
def user = new User()
user.username = params.nickname
user.email = params.email
user.openid = openidService.getIdentifier(session)
user.password = ''

if (user.save()) {
flash.message = "Thank you for signing up."
redirect(controller:'clickpass', action:'welcome')
}
else {
flash.message = "There is a problem signing up."
redirect(controller:'clickpass', action:'index')
}
}
}
  • index: Nothing for the index to do so set up a 404.
  • welcome: When a user is authenticated by OpenID, a search is made to see whether that OpenID is in the database, if not then redirect to the registration page. The urlString is obtained from Clickpass so this needs to be changed for each site
  • merge: A user, authenticated by OpenID, who wants to attach the ID to his account.
  • register: Create a new user account with the OpenID and save it.

User.groovy
Nothing in it except for the class declaration.

ClickpassTagLib.groovy
class ClickpassTagLib {
static namespace = "clickpass"

def bar = { attrs, body ->
try {
out << render(template:"/clickpass", contextPath:"${pluginContextPath}")
}
catch(Exception e) {
log.error(e)
}
}
}
  • The namespace allows one to use it, i.e <clickpass:foobar/>, in the code instead of the generic <g:foobar/>
  • For usage in an app, all that is required is <clickpass:bar/> to be inserted into the html.
  • The / in the template name is necessary to revert the path to the project base, otherwise it would go looking in the main app views folder.
  • The contextPath:"${pluginContextPath}" is not mentioned in the documention but after setting the path to the base, this sets it to the correct plugin views folder.

_clickpass.gsp
The html for this is generated by Clickpass, after registering your site, you can just copy and paste. This creates the Clickpass login bar.

welcome.gsp
<html>
<head>
<link rel="openid.server" href="http://www.clickpass.com/openid_server" />
<link rel="openid.delegate" href="http://clickpass.com/public/iseec" />
<title>Welcome</title>
</head>
<body>
<h1>Welcome</h1>
<openid:identifier />
<openid:ifLoggedIn>
<openid:logoutLink>Logout</openid:logoutLink>
</openid:ifLoggedIn>
</body>
</html>
  • This is a simple welcome page after the user successfully logs in and his account is verified.
  • One can replace it with their own or redirect from this page.
  • The OpenID is displayed as well as a logout link.

Aftermath
  • grails package-plugin to get it all into a zip file.
  • grails install-plugin /path/to/plugin/grails-example-0.1.zip to install it into an app.