vendredi 9 janvier 2009

New features in GraniteDS 1.2 : Tide support for Seam 2.1 authorizations

Seam 2.1 is the first release that is not tied to the JSF view technology. The Tide integration benefits from this with a little easier deployment (no more JSF jars and servlet configuration).

The migration to Seam 2.1 is very easy : replace granite-tide-seam.jar by granite-tide-seam21.jar and change the security service to
<security type="org.granite.seam21.security.Seam21SecurityService"/>.

Not saying that you also have to change the Seam jars...

Another important new feature of Seam 2.1 is the powerful security/authorization mechanism. We have updated the Seam security service to use the new APIs, but we have mainly added Flex support for server authorizations. The Tide identity component now provides the methods hasRole and hasPermission that request access rights from the server.

The most simple use of these methods is to conditionally enable/show parts of the UI in mxml components depending on user authorizations :

<mx:DataGrid id="dg" dataProvider="{products}"/>

<mx:Button id="bUpdate" label="Update"
enabled="{dg.selectedItem != null}"
visible="{identity.hasPermission(dg.selectedItem, 'update')}"
click="updateProduct()"/>
<mx:Button id="bDelete" label="Delete"
enabled="{dg.selectedItem != null}"
visible="{identity.hasRole('admin')}"
click="deleteProduct()"/>


Here the 'update' button will be showed only if the user has the rights to update the selected entity and the 'delete' button will be shown only to administrators.

The important thing to note is that these access rights are cached on the client and are not requested from the server at each call. This greatly reduces the performance penalty of using them. The hasPermission method is also integrated with the entity cache, meaning that the permission cache for entities is cleared whenever an entity is cleared from the cache.

It is possible to manually clear the cache with identity.clearSecurityCache(). For example you could setup a timer and call this periodically.

The identity.hasRole and identity.hasPermission methods can also be used programmatically in a controller :
Identity.hasRole and Identity.hasPermission can also be used programmatically. As the value may be retrieved asynchonously, the return value of these methods may not be accurate if it was not already in the cache (they return false by default). The correct way of handling this is to pass a result handler function:

public function canDelete(product:Product):void {
identity.hasRole('admin', canDeleteResult);
}

private function canDeleteResult(event:TideResultEvent, role:String):void {
if (!event.result)
Alert.show("You cannot delete the product");
}

public function canUpdate(product:Product):void {
identity.hasPermission(product, 'update', canUpdateResult);
}

private function canUpdateResult(event:TideResultEvent, product:Product, action:String):void {
if (!event.result)
Alert.show("You cannot update the product " + product.name);
}


As getting the result can necessitate a remote call, the hasPermission and hasRole take a result handler that is called when the result is available (immediately when the result is cached). Beware that the direct return value of these methods may not be correct when the data is not in cache.

The above example shows that the result handlers for hasRole and hasPermission have additional arguments that allow to know about the requested role or permission when many permissions are checked simultaneously.