Flex on Grails: Take 2, Part 2

This is a follow-up post to Flex on Grails, Take 2.

Task creation

Now that we have a basic working application, let’s improve it by adding some security in there. If your Grails application is still running, you can leave it that way as you won’t need to restart your backend every time you modify it. That’s the whole beauty and productivity of Grails and this plugin.

First thing we’re going to do is that we’re going to create a TaskDetail DTO that will be useful for our task creation method:

package org.epseelon.todolist.dto

class TaskDetail {
    Long id
    String title
}

Yes, I know, the fields are the same as for TaskListItem and you lazy ranters are already complaining about the fact that we create 2 identical classes. But remember that they serve completely different purposes, that we’re only starting, and that those 2 classes are very likely to evolve in very different ways. Having TaskDetail extend TaskListItem is also a bad idea because you would be assuming that all the fields in TaskListItem will always be needed in TaskDetail, which is not sure. For example, in a list, you might want to display a few summary fields that won’t be needed in a task edition form.

Notice that we created our TaskDetail class in the same package as TaskListItem, under src/groovy.

Now that we have our DTO, let’s add a new service operation below getAllTasks():

    @RemotingInclude
    @Secured(["ROLE_ADMIN"])
    TaskDetail createNewTask(TaskDetail newTask){
        Task task = new Task(title:newTask.title)
        if(task.validate()){
            task.save()
            return new TaskDetail(
                    id: task.id,
                    title: task.title
            )
        } else {
            throw new Exception(task.errors.allErrors.defaultMessage.join("n"))
        }
    }

Notice that in addition to RemotingInclude, we added a Secured annotation to our createNewTask operation. You should have two such annotations in your classpath. grails.plugins.springsecurity.Secured is used for securing Grails controller methods. Here we will be using org.springframework.security.access.annotation.Secured. The String array parameter of this annotation specifies a list of roles which means that whoever walls this method must be authenticated and have at least one of the roles listed in the annotation (at least that’s what the default security configuration does).

Now let’s go back to Flash Builder and improve our todolist frontend a little bit. First, let’s create a new TaskForm component to serve as a dialog box to create a new task. To do that, right click the default package and choose New>MXML Component:

Flex Component Creation

Let’s call our new component TaskForm and let’s make it based on spark.components.TitleWindow:

New MXML Component

Here is the code of the TaskForm component:

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" 
			   title="New Task" 
			   xmlns:valueObjects="valueObjects.*"
			   close="dispatchEvent(new Event('taskCancelled'))">
	<fx:Metadata>
		[Event(name="taskSaved",type="flash.events.Event")]
		[Event(name="taskCancelled",type="flash.events.Event")]
	</fx:Metadata>
	<fx:Script>
		<![CDATA[
			protected function cancelButton_clickHandler(event:MouseEvent):void
			{
				dispatchEvent(new Event("taskCancelled"));
			}

			protected function saveButton_clickHandler(event:MouseEvent):void
			{
				dispatchEvent(new Event("taskSaved"));
			}
		]]>
	</fx:Script>
	<fx:Declarations>
		<valueObjects:TaskDetail id="task" title="@{titleInput.text}"/>
	</fx:Declarations>
	<mx:Form width="100%" height="100%" defaultButton="{saveButton}">
		<mx:FormItem label="Title:" width="100%">
			<s:TextInput id="titleInput" width="100%"/>
		</mx:FormItem>
	</mx:Form>
	<s:controlBarContent>
		<s:HGroup width="100%" horizontalAlign="right">
			<s:Button id="cancelButton" label="Cancel" 
					  click="cancelButton_clickHandler(event)"/>
			<s:Button id="saveButton" label="Save" 
					  click="saveButton_clickHandler(event)"/>
 		</s:HGroup>
	</s:controlBarContent>
</s:TitleWindow>

Now let’s improve our Flex application to integrate that form and make it possible to create tasks. First off, let’s regenerate client stubs for our service. In Data/Services view, right-click TodoListService and choose Delete. I know, you would be tempted to click “Refresh”, but it doesn’t work with destinations exposed dynamically by Spring BlazeDS integration.

Delete Destination Menu

The next dialog allows you to select the files that will be deleted. Click OK to simply delete regenerated files:

Delete Destination

Click “Connect to Data/Service…” again, select todoListService and click OK. You should get the following service:

Data/Services

Then, modify the code of Main.xml like the following:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" 
			   xmlns:services="services.*"
			   minWidth="955" minHeight="600"
			   creationComplete="reloadTasks()">
	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.FlexEvent;
			import mx.managers.PopUpManager;
			import mx.rpc.events.ResultEvent;
			
			private var taskForm:TaskForm;
			
			private function reloadTasks():void {
				getAllTasksResult.token = todoListService.getAllTasks();
			}

			protected function createTaskButton_clickHandler(event:MouseEvent):void {
				taskForm = new TaskForm();
				taskForm.addEventListener("taskSaved", taskSaved);
				taskForm.addEventListener("taskCancelled", taskCancelled);
				PopUpManager.addPopUp(taskForm, this, true);
				PopUpManager.centerPopUp(taskForm);
			}
			
			private function taskSaved(event:Event):void{
				createTaskResult.token = todoListService.createNewTask(taskForm.task);
			}
			
			private function taskCancelled(event:Event):void {
				PopUpManager.removePopUp(taskForm);
				taskForm = null;
			}

			protected function createTaskResult_resultHandler(event:ResultEvent):void {
				PopUpManager.removePopUp(taskForm);
				taskForm = null;
				reloadTasks();
			}
		]]>
	</fx:Script>
	<fx:Declarations>
		<s:CallResponder id="getAllTasksResult"/>
		<s:CallResponder id="createTaskResult" 
						 result="createTaskResult_resultHandler(event)"/>
		<services:TodoListService id="todoListService" 
			fault="Alert.show(event.fault.faultString + 'n' + event.fault.faultDetail)" 
			showBusyCursor="true"/>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>
	<s:Panel width="100%" height="100%" title="Todo List">
		<s:List width="100%" height="100%" id="list" labelField="title">
			<s:AsyncListView list="{getAllTasksResult.lastResult}"/>
		</s:List>
		<s:controlBarContent>
			<s:Button id="createTaskButton" label="+" 
					  fontWeight="bold" fontSize="20"
					  click="createTaskButton_clickHandler(event)"/>
		</s:controlBarContent>
	</s:Panel>
</s:Application>

With this new code, when you click the “+” button, a dialog box opens, lets you specify the title of your task, and when you click Save, it calls the createNewTask() method of our service. And that’s where security jumps in, because you get an error dialog saying “Access is denied”.

Access is denied

And that’s alright, because we are not even authenticated, let alone authorized.

Spring Security

First off, we need to create security-related domain class. In the Grails application, run the following command: “grails s2-quickstart org.epseelon.todolist.domain User Role”. To run this command in IntelliJ, you have to bring up the groovy command dialog by using the Tools menu and then choose Grails>Target:

And type “s2-quickstart org.epseelon.todolist.domain User Role” in the dialog that appears.

This command generates 3 classes – User, Role and UserRole – in the org.epseelon.todolist.domain package. It also generates 4 classes in org.codehaus.groovy.grails.plugins.springsecurity.acl but to be honest, I don’t really know what these classes are for.

Now that we have our security classes, let’s create a couple of users and roles in BootStrap.groovy.

import org.epseelon.todolist.domain.Role
import org.epseelon.todolist.domain.Task
import org.epseelon.todolist.domain.User
import org.epseelon.todolist.domain.UserRole

class BootStrap {

    def springSecurityService

    def init = { servletContext ->
        def admin = new User(username: "admin", password: springSecurityService.encodePassword("admin"), enabled: true).save()
        def user = new User(username: "user", password: springSecurityService.encodePassword("user"), enabled: true).save()

        def adminRole = new Role(authority: "ROLE_ADMIN").save()
        def userRole = new Role(authority: "ROLE_USER").save()

        new UserRole(user: user, role: userRole).save()
        new UserRole(user: admin, role: adminRole).save()

        new Task(title: "Task 1").save()
        new Task(title: "Task 2").save()
        new Task(title: "Task 3").save()
    }

    def destroy = {
    }
}

As you can see, we are creating 2 users, originally called “admin” and “user”, and 2 corresponding roles.

Don’t forget to restart your backend NOW in order to take those modifications into account.

Authentication UI

Now, let’s move back to Flash Builder to add some authentication UI.

First, let’s create 2 states for our main UI, a “loggedIn” and a “loggedOut” state. In Main.mxml, add the following code between the fx:Script and the fx:Declarations sections:

<s:states>
	<s:State name="loggedOut"/>
	<s:State name="loggedIn"/>
</s:states>

In Flex, authentication is handled via the channelSet associated to a RemoteObject, so we have to update our TodoListService instantiation code like the following:

<services:TodoListService id="todoListService" 
		fault="Alert.show(event.fault.faultString + 'n' + event.fault.faultDetail)" 
		showBusyCursor="true">
	<services:channelSet>
		<s:ChannelSet id="channelSet">
			<s:AMFChannel url="http://localhost:8080/todolist/messagebroker/amf"/>
		</s:ChannelSet>
	</services:channelSet>
</services:TodoListService>

Yes, I know, the URL is now hard-coded inside the application but we’ll improve that in a future episode by integrating some dependency injection logic.

Then add the currentState attribute to the Application element:

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" 
			   xmlns:services="services.*"
			   minWidth="955" minHeight="600"
			   creationComplete="reloadTasks()" 
			   currentState="{channelSet.authenticated ? 'loggedIn' : 'loggedOut'}">
...
</s:Application>

Now add a String variable to Main.mxml that will contain the name of the user currently logged in:

[sourcecode language=”actionscript”]
[Bindable]
private var loggedInUser:String;
[/sourcecode]

And add some authentication UI to the control bar of our main panel:

<s:controlBarContent>
	<s:Button id="createTaskButton" label="+" 
			  fontWeight="bold" fontSize="20"
			  click="createTaskButton_clickHandler(event)"/>
	<s:HGroup width="100%" horizontalAlign="right" includeIn="loggedOut">
		<s:Label text="User name: "/>
		<s:TextInput id="usernameInput"/>
		<s:Label text="Password: "/>
		<s:TextInput id="passwordInput" displayAsPassword="true"/>
		<s:Button id="loginButton" label="Login" click="loginButton_clickHandler(event)"/>
	</s:HGroup>
	<s:HGroup width="100%" horizontalAlign="right" includeIn="loggedIn">
		<s:Label text="Logged in as {loggedInUser}"/>
		<s:Button id="logoutButton" label="Logout" click="logoutButton_clickHandler(event)"/>
	</s:HGroup>
</s:controlBarContent>

Before implementing login and logout logic, let’s add 2 CallResponder’s to handle responses to login and logout:

<s:CallResponder id="loginResult" result="loginResult_resultHandler(event)" 
				 fault="Alert.show(event.fault.faultString + 'n' + event.fault.faultDetail)"/>
<s:CallResponder id="logoutResult" result="logoutResult_resultHandler(event)" 
				 fault="Alert.show(event.fault.faultString + 'n' + event.fault.faultDetail)"/>

Now let’s implement the click handlers for our login and logout buttons:

protected function loginButton_clickHandler(event:MouseEvent):void {
	loginResult.token = channelSet.login(usernameInput.text, passwordInput.text);
}

protected function logoutButton_clickHandler(event:MouseEvent):void {
	logoutResult.token = channelSet.logout();
}

protected function loginResult_resultHandler(event:ResultEvent):void {
	loggedInUser = event.result.name;
	usernameInput.text = "";
	passwordInput.text = "";
}

protected function logoutResult_resultHandler(event:ResultEvent):void {
	loggedInUser = "";
}

Here is the entire code of our Main.mxml at this stage:


<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" 
			   xmlns:services="services.*"
			   minWidth="955" minHeight="600"
			   creationComplete="reloadTasks()" 
			   currentState="{channelSet.authenticated ? 'loggedIn' : 'loggedOut'}">
	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.FlexEvent;
			import mx.managers.PopUpManager;
			import mx.rpc.events.ResultEvent;
			
			private var taskForm:TaskForm;
			
			[Bindable]
			private var loggedInUser:String;
			
			private function reloadTasks():void {
				getAllTasksResult.token = todoListService.getAllTasks();
			}

			protected function createTaskButton_clickHandler(event:MouseEvent):void {
				taskForm = new TaskForm();
				taskForm.addEventListener("taskSaved", taskSaved);
				taskForm.addEventListener("taskCancelled", taskCancelled);
				PopUpManager.addPopUp(taskForm, this, true);
				PopUpManager.centerPopUp(taskForm);
			}
			
			private function taskSaved(event:Event):void{
				createTaskResult.token = todoListService.createNewTask(taskForm.task);
			}
			
			private function taskCancelled(event:Event):void {
				PopUpManager.removePopUp(taskForm);
				taskForm = null;
			}

			protected function createTaskResult_resultHandler(event:ResultEvent):void {
				PopUpManager.removePopUp(taskForm);
				taskForm = null;
				reloadTasks();
			}

			protected function loginButton_clickHandler(event:MouseEvent):void
			{
				loginResult.token = channelSet.login(usernameInput.text, passwordInput.text);
			}

			protected function logoutButton_clickHandler(event:MouseEvent):void
			{
				logoutResult.token = channelSet.logout();
			}

			protected function loginResult_resultHandler(event:ResultEvent):void
			{
				loggedInUser = event.result.name;
				usernameInput.text = "";
				passwordInput.text = "";
			}

			protected function logoutResult_resultHandler(event:ResultEvent):void
			{
				loggedInUser = "";
			}
		]]>
	</fx:Script>
	<s:states>
		<s:State name="loggedOut"/>
		<s:State name="loggedIn"/>
	</s:states>
	<fx:Declarations>
		<s:CallResponder id="getAllTasksResult"/>
		<s:CallResponder id="createTaskResult" 
						 result="createTaskResult_resultHandler(event)"/>
		<s:CallResponder id="loginResult" result="loginResult_resultHandler(event)" 
						 fault="Alert.show(event.fault.faultString + 'n' + event.fault.faultDetail)"/>
		<s:CallResponder id="logoutResult" result="logoutResult_resultHandler(event)" 
						 fault="Alert.show(event.fault.faultString + 'n' + event.fault.faultDetail)"/>
		<services:TodoListService id="todoListService" 
			fault="Alert.show(event.fault.faultString + 'n' + event.fault.faultDetail)" 
			showBusyCursor="true">
			<services:channelSet>
				<s:ChannelSet id="channelSet">
					<s:AMFChannel url="http://localhost:8080/todolist/messagebroker/amf"/>
				</s:ChannelSet>
			</services:channelSet>
		</services:TodoListService>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>
	<s:Panel width="100%" height="100%" title="Todo List">
		<s:List width="100%" height="100%" id="list" labelField="title">
			<s:AsyncListView list="{getAllTasksResult.lastResult}"/>
		</s:List>
		<s:controlBarContent>
			<s:Button id="createTaskButton" label="+" 
					  fontWeight="bold" fontSize="20"
					  click="createTaskButton_clickHandler(event)"/>
			<s:HGroup width="100%" horizontalAlign="right" includeIn="loggedOut">
				<s:Label text="User name: "/>
				<s:TextInput id="usernameInput"/>
				<s:Label text="Password: "/>
				<s:TextInput id="passwordInput" displayAsPassword="true"/>
				<s:Button id="loginButton" label="Login" click="loginButton_clickHandler(event)"/>
			</s:HGroup>
			<s:HGroup width="100%" horizontalAlign="right" includeIn="loggedIn">
				<s:Label text="Logged in as {loggedInUser}"/>
				<s:Button id="logoutButton" label="Logout" click="logoutButton_clickHandler(event)"/>
			</s:HGroup>
		</s:controlBarContent>
	</s:Panel>
</s:Application>

Running the application

Now when you run your application (and provided that you restarted the backend after adding security initialization code), you should get the following results:

  • If you don’t log in and try to create a new task, you’ll get an “Access is denied” message like before
  • If you log in as user/user and try to create a new task, you’ll get an “Access is denied message because authorization is kicking in and you don’t have the ROLE_ADMIN role
  • If you log in as admin/admin and try to create a new task, it works!

That’s it! You’ve secured your first Flex on Grails service using Grails BlazeDS plugin version 2.0 and Spring Security.

Here is the entire code of the todolist application at this stage.

In the next episode, we will clean up the frontend a little bit by introducing some dependency injection into the mix.

15 comments

  1. Have you managed to solve any lazy load exceptions with this approach that you get with the underlying GORM/Hibernate? – You don’t have a complicated enough domain model here yet to warrant knowing if this approach solves anything new. (Or am I missing something?)
    Spice Factory Pimento/Parsley still looks like best approach.
    Possibly Robotlegs/dpHibernate – Michael Labriola recently mentioned they’ve got an improved version of dpHibernate that circumvents need for having domain class inherit from a base class. I also think something Yakov Fain did in Clear Toolkit could be adapted (he was using some XDoclet style approach with SQL- but the underlying concept of sending the delta changes over the wire I reckon could be applied to Hibernate

    1. As far as I’m concerned, I’ve never faced any issue with lazy loading because I’ve always been a firm opponent of using lazy loading over the wire, which is why I always work with DTO’s. I think that the Flex plugin adds something to that but I’m not using it so you might want to wait for Burt Beckwith to publish his screencast showing his blazeds+flex plugin setup.

  2. Jeremy – I looked at dpHibernate but didn’t like having to use subclasses, but I’m glad to know they’re working on that. Spring Flex has converters that handle lazy loading and send null for uninitialized references or collections. I made those configurable and added an OpenSessionInView-style wrapper (which can be disabled) so you can use lazy loading if you want, or not use it and send nothing, or explicitly initialize lazy-loaded references.

    DTOs are a good solution, but they have the one disadvantage of not being very DRY.

    See “Data serialization” in section 3 in the docs for more info: http://grails-plugins.github.com/grails-blazeds/docs/manual/

    1. DTO’s are DRY! Because transferring data and storing it are 2 different beasts. DTO’s are more secure (you transfer only the data that is needed), take less bandwidth (for the same reason) and make your UI less tightly-coupled to your domain model. For me that’s worth a few copy/pastes. Don’t you think?

  3. ¡DTOs are not DRY!, they are very much RY (or perhaps wet). Change domain => change DTO + rewrite code where you populate your DTO. Ouch.

    If the main arguments for DTOs are what you have stated above then I’m definitely not convinced. Well, each to their own maybe one day I’ll be converted, in the meantime I’m looking forward to the mentioned screencast from Burt. This new BlazeDS/Grails integration is all great to read about.

  4. A change in your DTO does not always imply a change in your user interface, hence it does not always require a change in your DTO’s. And when it does, well, you are simply changing a feature in your app, which certainly implies changes in all the layers of your application, so I don’t see where is the RY. And even if there is some RY, I personnally think that loose coupling is much more important for maintainability and evolutivity than lazin… huuuh… sorry… DRY. ;o)

  5. What is the problem with doing just that? Behind the scenes, BlazeDS is invoking your service methods exactly like a controller would so you should have no problem overriding invokemethod.

  6. Hi, I was just wondering how secure a flex application would be if you wrap the SWF in a gsp, and configure Spring Security in the usual way you would do for a html based grails app. Once the html wrapped SWF is loaded onto the browser, you are redirected to the spring security login page. Upon login you’re redirected to the home page of your app which contains the swf with the bulk of the functionality.

    You’re services are anyway secured and enabled for remote access and spring would throw an exception if you’re not logged in. This gives you a cookie and session id based authentication mechanism with all the easy to do parts of spring security without you worrying about any of the intricate detail involved.

    I tried this out and it seems to work. But I was just wondering if I’m over looking some obvious but important security concern. Does anyone see problems with this approach?

    Thanks

  7. First of all: Great series of articles! Thanks for sharing.
    But I keep getting Bad Credentials error when trying to login with user/user or admin/admin.
    I’ve deleted the project and started over and it’s the same.
    I’m using latest versions.
    Grails 1.3.7
    Flex 4.5.1
    and STS IDE
    I’m a Flex/PHP developer, and this is the first time I’m trying to work with Java, so I’m really lost here.
    Any idea of what is wrong?

  8. Found the problem.
    My version of Spring Security automaticaly created:
    def beforeInsert() { encodePassword() }

    So it always encode the password property when set.

    And in the Bootstrap example it has:
    def user = new User(username: “user”, password: springSecurityService.encodePassword(“user”), enabled: true).save()

    So it was encoding the password twice.

  9. I found a bug. The password must be encrypted with SHA256.
    So you should write as follows.

    import mx.utils.SHA256;
    import flash.utils.ByteArray;

    protected function loginButton_clickHandler(event:MouseEvent):void
    {
    var bytes:ByteArray = new ByteArray();
    bytes.writeUTFBytes(passwordInput.text);
    loginResult.token = channelSet.login(usernameInput.text, SHA256.computeDigest(bytes));
    }

Leave a Reply to Daniel Georgii Cancel reply